summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--auto_tests/toxav_basic_test.c1033
-rw-r--r--auto_tests/toxav_many_test.c610
-rw-r--r--configure.ac14
-rw-r--r--other/apidsl/toxav.in.h615
-rw-r--r--testing/av_test.c731
-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.c1837
-rw-r--r--toxav/msi.h191
-rw-r--r--toxav/rtp.c688
-rw-r--r--toxav/rtp.h166
-rw-r--r--toxav/toxav.c1506
-rw-r--r--toxav/toxav.h765
-rw-r--r--toxav/toxav_old.c81
-rw-r--r--toxav/video.c267
-rw-r--r--toxav/video.h67
-rw-r--r--toxcore/Messenger.c6
-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/util.c82
-rw-r--r--toxcore/util.h13
30 files changed, 6033 insertions, 4406 deletions
diff --git a/auto_tests/toxav_basic_test.c b/auto_tests/toxav_basic_test.c
index af8d91e9..5821a6d4 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
31 39#define TEST_REGULAR_AV 1
32typedef enum _CallStatus { 40#define TEST_REGULAR_A 1
33 none, 41#define TEST_REGULAR_V 1
34 InCall, 42#define TEST_REJECT 1
35 Ringing, 43#define TEST_CANCEL 1
36 Ended, 44#define TEST_MUTE_UNMUTE 1
37 Rejected, 45#define TEST_STOP_RESUME_PAYLOAD 1
38 Canceled, 46#define TEST_PAUSE_RESUME_SEND 1
39 TimedOut
40
41} CallStatus;
42
43typedef struct _Party {
44 CallStatus status;
45 ToxAv *av;
46 time_t *CallStarted;
47 int call_index;
48} Party;
49
50typedef struct _Status {
51 Party Alice;
52 Party Bob;
53} Status;
54
55/* My default settings */
56static ToxAvCSettings muhcaps;
57
58void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
59{
60 if (length == 7 && memcmp("gentoo", data, 7) == 0) {
61 tox_friend_add_norequest(m, public_key, 0);
62 }
63}
64
65
66/******************************************************************************/
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}
79void callback_recv_ringing ( void *av, int32_t call_index, void *_arg )
80{
81 Status *cast = _arg;
82
83 if (cast->Alice.av == av) {
84 /* Alice always sends invite */
85 cast->Alice.status = Ringing;
86 } else if (cast->Bob.av == av) {
87 // ...
88 }
89}
90 47
91 48
92void callback_call_started ( void *av, int32_t call_index, void *_arg ) 49typedef struct {
93{ 50 bool incoming;
94 Status *cast = _arg; 51 uint32_t state;
52
53} CallControl;
95 54
96 if (cast->Alice.av == av) {
97 printf("Call started on Alices side...\n");
98 cast->Alice.status = InCall;
99 toxav_prepare_transmission(av, call_index, 1);
100 } else if (cast->Bob.av == av) {
101 printf("Call started on Bob side...\n");
102 cast->Bob.status = InCall;
103 toxav_prepare_transmission(av, call_index, 1);
104 }
105}
106void callback_call_canceled ( void *av, int32_t call_index, void *_arg )
107{
108 Status *cast = _arg;
109 55
110 if (cast->Alice.av == av) { 56/**
111 // ... 57 * Callbacks
112 } else if (cast->Bob.av == av) { 58 */
113 printf ( "Call Canceled for Bob!\n" ); 59void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
114 cast->Bob.status = Canceled;
115 }
116}
117void callback_call_rejected ( void *av, int32_t call_index, void *_arg )
118{ 60{
119 Status *cast = _arg; 61 (void) av;
120 62 (void) friend_number;
121 printf ( "Call rejected by Bob!\n" 63 (void) audio_enabled;
122 "Call ended for Alice!\n" ); 64 (void) video_enabled;
123 65
124 /* If Bob rejects, call is ended for alice and she sends ending */ 66 printf("Handling CALL callback\n");
125 if (cast->Alice.av == av) { 67 ((CallControl*)user_data)->incoming = true;
126 cast->Alice.status = Rejected;
127 } else if (cast->Bob.av == av) {
128 //... ignor
129 }
130} 68}
131void callback_call_ended ( 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)
132{ 70{
133 Status *cast = _arg; 71 (void) av;
134 72 (void) friend_number;
135 if (cast->Alice.av == av) { 73
136 printf ( "Call ended for Alice!\n" ); 74 printf("Handling CALL STATE callback: %d\n", state);
137 cast->Alice.status = Ended; 75 ((CallControl*)user_data)->state = state;
138 } else if (cast->Bob.av == av) {
139 printf ( "Call ended for Bob!\n" );
140 cast->Bob.status = Ended;
141 }
142} 76}
143 77void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
144void callback_peer_cs_change ( void *av, int32_t call_index, void *_arg ) 78 uint16_t width, uint16_t height,
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)
145{ 82{
146 ToxAvCSettings csettings; 83 (void) av;
147 toxav_get_peer_csettings(av, call_index, 0, &csettings); 84 (void) friend_number;
148 85 (void) width;
149 printf("Peer changing settings to: \n" 86 (void) height;
150 "Type: %u \n" 87 (void) y;
151 "Video bitrate: %u \n" 88 (void) u;
152 "Video height: %u \n" 89 (void) v;
153 "Video width: %u \n" 90 (void) ystride;
154 "Audio bitrate: %u \n" 91 (void) ustride;
155 "Audio framedur: %u \n" 92 (void) vstride;
156 "Audio sample rate: %u \n" 93 (void) user_data;
157 "Audio channels: %u \n", 94 printf("Received video payload\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} 95}
168 96void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
169void callback_self_cs_change ( void *av, int32_t call_index, void *_arg ) 97 int16_t const *pcm,
98 size_t sample_count,
99 uint8_t channels,
100 uint32_t sampling_rate,
101 void *user_data)
170{ 102{
171 ToxAvCSettings csettings; 103 (void) av;
172 toxav_get_peer_csettings(av, call_index, 0, &csettings); 104 (void) friend_number;
173 105 (void) pcm;
174 printf("Changed settings to: \n" 106 (void) sample_count;
175 "Type: %u \n" 107 (void) channels;
176 "Video bitrate: %u \n" 108 (void) sampling_rate;
177 "Video height: %u \n" 109 (void) user_data;
178 "Video width: %u \n" 110 printf("Received audio payload\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} 111}
193 112void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
194void callback_requ_timeout ( void *av, int32_t call_index, void *_arg )
195{ 113{
196 Status *cast = _arg; 114 (void) userdata;
197 printf("Call timed-out!\n"); 115
198 116 if (length == 7 && memcmp("gentoo", data, 7) == 0) {
199 if (cast->Alice.av == av) { 117 ck_assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0);
200 cast->Alice.status = TimedOut;
201 } else if (cast->Bob.av == av) {
202 cast->Bob.status = TimedOut;
203 } 118 }
204} 119}
205 120
206void callback_audio (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data)
207{}
208
209void callback_video (void *agent, int32_t call_idx, const vpx_image_t *img, void *data)
210{}
211 121
212void register_callbacks(ToxAv *av, void *data) 122/**
123 * Iterate helper
124 */
125int iterate_tox(Tox* bootstrap, Tox* Alice, Tox* Bob)
213{ 126{
214 toxav_register_callstate_callback(av, callback_call_started, av_OnStart, data); 127 tox_iterate(bootstrap);
215 toxav_register_callstate_callback(av, callback_call_canceled, av_OnCancel, data); 128 tox_iterate(Alice);
216 toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data); 129 tox_iterate(Bob);
217 toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data); 130
218 toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data); 131 return MIN(tox_iteration_interval(Alice), tox_iteration_interval(Bob));
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} 132}
226 133
227 134
228/*************************************************************************************************/
229
230/* Alice calls bob and the call starts.
231 * What happens during the call is defined after. To quit the loop use: step++;
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 135
250START_TEST(test_AV_flows) 136START_TEST(test_AV_flows)
251{ 137{
138 Tox* Alice, *Bob, *bootstrap;
139 ToxAV* AliceAV, *BobAV;
140
141 CallControl AliceCC, BobCC;
142
143 {
144 TOX_ERR_NEW error;
145
146 bootstrap = tox_new(NULL, &error);
147 ck_assert(error == TOX_ERR_NEW_OK);
148
149 Alice = tox_new(NULL, &error);
150 ck_assert(error == TOX_ERR_NEW_OK);
151
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");
252 long long unsigned int cur_time = time(NULL); 158 long long unsigned int cur_time = time(NULL);
253 Tox *bootstrap_node = tox_new(0, 0); 159
254 Tox *Alice = tox_new(0, 0);
255 Tox *Bob = tox_new(0, 0);
256
257 ck_assert_msg(bootstrap_node || Alice || Bob, "Failed to create 3 tox instances");
258
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); 165
264 166
265 ck_assert_msg(test == 0, "Failed to add friend error code: %i", test); 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); 173
272 tox_iterate(Bob); 174 if (tox_self_get_connection_status(bootstrap) &&
273 175 tox_self_get_connection_status(Alice) &&
274 if (tox_self_get_connection_status(bootstrap_node) && tox_self_get_connection_status(Alice) 176 tox_self_get_connection_status(Bob) && off) {
275 && tox_self_get_connection_status(Bob)
276 && 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); 188
288 189 {
289 muhcaps = av_DefaultSettings; 190 TOXAV_ERR_NEW error;
290 muhcaps.max_video_height = muhcaps.max_video_width = 128; 191 AliceAV = toxav_new(Alice, &error);
291 192 ck_assert(error == TOXAV_ERR_NEW_OK);
292 Status status_control = { 193
293 {none, toxav_new(Alice, 1), NULL, -1}, 194 BobAV = toxav_new(Bob, &error);
294 {none, toxav_new(Bob, 1), NULL, -1}, 195 ck_assert(error == TOXAV_ERR_NEW_OK);
295 }; 196 }
296 197
297 ck_assert_msg(status_control.Alice.av || status_control.Bob.av, "Failed to create 2 toxav instances"); 198 toxav_callback_call(AliceAV, t_toxav_call_cb, &AliceCC);
298 199 toxav_callback_call_state(AliceAV, t_toxav_call_state_cb, &AliceCC);
299 200 toxav_callback_video_receive_frame(AliceAV, t_toxav_receive_video_frame_cb, &AliceCC);
300 register_callbacks(status_control.Alice.av, &status_control); 201 toxav_callback_audio_receive_frame(AliceAV, t_toxav_receive_audio_frame_cb, &AliceCC);
301 register_callbacks(status_control.Bob.av, &status_control); 202
302 203 toxav_callback_call(BobAV, t_toxav_call_cb, &BobCC);
303 const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000); 204 toxav_callback_call_state(BobAV, t_toxav_call_state_cb, &BobCC);
304 int16_t sample_payload[frame_size]; 205 toxav_callback_video_receive_frame(BobAV, t_toxav_receive_video_frame_cb, &BobCC);
305 randombytes((uint8_t *)sample_payload, sizeof(int16_t) * frame_size); 206 toxav_callback_audio_receive_frame(BobAV, t_toxav_receive_audio_frame_cb, &BobCC);
306 207
307 uint8_t prepared_payload[RTP_PAYLOAD_SIZE]; 208 printf("Created 2 instances of ToxAV\n");
308 int payload_size; 209 printf("All set after %llu seconds!\n", time(NULL) - cur_time);
309 210
310 vpx_image_t *sample_image = vpx_img_alloc(NULL, VPX_IMG_FMT_I420, 128, 128, 1); 211
311 212#define REGULAR_CALL_FLOW(A_BR, V_BR) \
312 memcpy(sample_image->planes[VPX_PLANE_Y], sample_payload, 10); 213 do { \
313 memcpy(sample_image->planes[VPX_PLANE_U], sample_payload, 10); 214 memset(&AliceCC, 0, sizeof(CallControl)); \
314 memcpy(sample_image->planes[VPX_PLANE_V], sample_payload, 10); 215 memset(&BobCC, 0, sizeof(CallControl)); \
315 216 \
316 217 TOXAV_ERR_CALL rc; \
317 /************************************************************************************************* 218 toxav_call(AliceAV, 0, A_BR, V_BR, &rc); \
318 * Successful flows (when call starts) 219 \
319 */ 220 if (rc != TOXAV_ERR_CALL_OK) { \
320 221 printf("toxav_call failed: %d\n", rc); \
321 /* 222 ck_assert(0); \
322 * Call with audio only on both sides. Alice calls Bob. 223 } \
323 */ 224 \
324 225 \
325 226 long long unsigned int start_time = time(NULL); \
326 CALL_AND_START_LOOP(TypeAudio, TypeAudio) { 227 \
327 /* Both send */ 228 \
328 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, 229 while (BobCC.state != TOXAV_FRIEND_CALL_STATE_FINISHED) { \
329 1000, sample_payload, frame_size); 230 \
330 231 if (BobCC.incoming) { \
331 if ( payload_size < 0 ) { 232 TOXAV_ERR_ANSWER rc; \
332 ck_assert_msg ( 0, "Failed to encode payload" ); 233 toxav_answer(BobAV, 0, A_BR, V_BR, &rc); \
333 } 234 \
334 235 if (rc != TOXAV_ERR_ANSWER_OK) { \
335 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); 236 printf("toxav_answer failed: %d\n", rc); \
336 237 ck_assert(0); \
337 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, 238 } \
338 sample_payload, frame_size); 239 BobCC.incoming = false; \
339 240 } else { \
340 if ( payload_size < 0 ) { 241 /* TODO rtp */ \
341 ck_assert_msg ( 0, "Failed to encode payload" ); 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 }
264
265 if (TEST_REGULAR_A) {
266 printf("\nTrying regular call (Audio only)...\n");
267 REGULAR_CALL_FLOW(48, 0);
268 }
269
270 if (TEST_REGULAR_V) {
271 printf("\nTrying regular call (Video only)...\n");
272 REGULAR_CALL_FLOW(0, 4000);
273 }
274
275#undef REGULAR_CALL_FLOW
276
277 if (TEST_REJECT) { /* Alice calls; Bob rejects */
278 printf("\nTrying reject flow...\n");
279
280 memset(&AliceCC, 0, sizeof(CallControl));
281 memset(&BobCC, 0, sizeof(CallControl));
282
283 {
284 TOXAV_ERR_CALL rc;
285 toxav_call(AliceAV, 0, 48, 0, &rc);
286
287 if (rc != TOXAV_ERR_CALL_OK) {
288 printf("toxav_call failed: %d\n", rc);
289 ck_assert(0);
290 }
342 } 291 }
343 292
344 toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); 293 while (!BobCC.incoming)
345 294 iterate_tox(bootstrap, Alice, Bob);
346 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ 295
347 step++; /* This terminates the loop */ 296 /* Reject */
348 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); 297 {
349 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); 298 TOXAV_ERR_CALL_CONTROL rc;
350 299 toxav_call_control(BobAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
351 /* Call over Alice hangs up */ 300
352 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); 301 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
302 printf("toxav_call_control failed: %d\n", rc);
303 ck_assert(0);
304 }
353 } 305 }
306
307 while (AliceCC.state != TOXAV_FRIEND_CALL_STATE_FINISHED)
308 iterate_tox(bootstrap, Alice, Bob);
309
310 printf("Success!\n");
354 } 311 }
355 TERMINATE_SCOPE() 312
356 313 if (TEST_CANCEL) { /* Alice calls; Alice cancels while ringing */
357 314 printf("\nTrying cancel (while ringing) flow...\n");
358 /* 315
359 * Call with audio on both sides and video on one side. Alice calls Bob. 316 memset(&AliceCC, 0, sizeof(CallControl));
360 */ 317 memset(&BobCC, 0, sizeof(CallControl));
361 CALL_AND_START_LOOP(TypeAudio, TypeVideo) { 318
362 /* Both send */ 319 {
363 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, 320 TOXAV_ERR_CALL rc;
364 1000, sample_payload, frame_size); 321 toxav_call(AliceAV, 0, 48, 0, &rc);
365 322
366 if ( payload_size < 0 ) { 323 if (rc != TOXAV_ERR_CALL_OK) {
367 ck_assert_msg ( 0, "Failed to encode payload" ); 324 printf("toxav_call failed: %d\n", rc);
368 } 325 ck_assert(0);
369 326 }
370 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size);
371
372 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000,
373 sample_payload, frame_size);
374
375 if ( payload_size < 0 ) {
376 ck_assert_msg ( 0, "Failed to encode payload" );
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); 331
382 332 /* Cancel */
383 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ 333 {
384 step++; /* This terminates the loop */ 334 TOXAV_ERR_CALL_CONTROL rc;
385 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); 335 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
386 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); 336
387 337 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
388 /* Call over Alice hangs up */ 338 printf("toxav_call_control failed: %d\n", rc);
389 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); 339 ck_assert(0);
340 }
390 } 341 }
342
343 /* Alice will not receive end state */
344 while (BobCC.state != TOXAV_FRIEND_CALL_STATE_FINISHED)
345 iterate_tox(bootstrap, Alice, Bob);
346
347 printf("Success!\n");
391 } 348 }
392 TERMINATE_SCOPE() 349
393 350 if (TEST_MUTE_UNMUTE) { /* Check Mute-Unmute etc */
394 351 printf("\nTrying mute functionality...\n");
395 /* 352
396 * Call with audio and video on both sides. Alice calls Bob. 353 memset(&AliceCC, 0, sizeof(CallControl));
397 */ 354 memset(&BobCC, 0, sizeof(CallControl));
398 CALL_AND_START_LOOP(TypeVideo, TypeVideo) { 355
399 /* Both send */ 356 /* Assume sending audio and video */
400 357 {
401 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, 358 TOXAV_ERR_CALL rc;
402 1000, sample_payload, frame_size); 359 toxav_call(AliceAV, 0, 48, 1000, &rc);
403 360
404 if ( payload_size < 0 ) { 361 if (rc != TOXAV_ERR_CALL_OK) {
405 ck_assert_msg ( 0, "Failed to encode payload" ); 362 printf("toxav_call failed: %d\n", rc);
363 ck_assert(0);
364 }
406 } 365 }
407 366
408 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); 367 while (!BobCC.incoming)
409 368 iterate_tox(bootstrap, Alice, Bob);
410 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, 369
411 sample_payload, frame_size); 370 /* At first try all stuff while in invalid state */
412 371 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL));
413 if ( payload_size < 0 ) { 372 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
414 ck_assert_msg ( 0, "Failed to encode payload" ); 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));
377
378 {
379 TOXAV_ERR_ANSWER rc;
380 toxav_answer(BobAV, 0, 48, 4000, &rc);
381
382 if (rc != TOXAV_ERR_ANSWER_OK) {
383 printf("toxav_answer failed: %d\n", rc);
384 ck_assert(0);
385 }
415 } 386 }
416 387
417 toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); 388 iterate_tox(bootstrap, Alice, Bob);
418 389
419// toxav_send_video(status_control.Alice.av, status_control.Alice.call_index, sample_image); 390 /* Pause and Resume */
420// toxav_send_video(status_control.Bob.av, status_control.Bob.call_index, sample_image); 391 printf("Pause and Resume\n");
421 392 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL));
422 393 iterate_tox(bootstrap, Alice, Bob);
423 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ 394 ck_assert(BobCC.state == 0);
424 step++; /* This terminates the loop */ 395 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
425 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); 396 iterate_tox(bootstrap, Alice, Bob);
426 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); 397 ck_assert(BobCC.state & (TOXAV_FRIEND_CALL_STATE_SENDING_A | TOXAV_FRIEND_CALL_STATE_SENDING_V));
427 398
428 /* Call over Alice hangs up */ 399 /* Mute/Unmute single */
429 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); 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 }
430 } 431 }
432
433 iterate_tox(bootstrap, Alice, Bob);
434 ck_assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
435
436 printf("Success!\n");
431 } 437 }
432 TERMINATE_SCOPE() 438
433 439 if (TEST_STOP_RESUME_PAYLOAD) { /* Stop and resume audio/video payload */
434 440 printf("\nTrying stop/resume functionality...\n");
435 uint64_t times_they_are_a_changin = time(NULL); 441
436 /* Media change */ 442 memset(&AliceCC, 0, sizeof(CallControl));
437 CALL_AND_START_LOOP(TypeAudio, TypeAudio) { 443 memset(&BobCC, 0, sizeof(CallControl));
438 /* Both send */ 444
439 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, 445 /* Assume sending audio and video */
440 1000, sample_payload, frame_size); 446 {
441 447 TOXAV_ERR_CALL rc;
442 if ( payload_size < 0 ) { 448 toxav_call(AliceAV, 0, 48, 0, &rc);
443 ck_assert_msg ( 0, "Failed to encode payload" ); 449
444 } 450 if (rc != TOXAV_ERR_CALL_OK) {
445 451 printf("toxav_call failed: %d\n", rc);
446 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); 452 ck_assert(0);
447 453 }
448 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000,
449 sample_payload, frame_size);
450
451 if ( payload_size < 0 ) {
452 ck_assert_msg ( 0, "Failed to encode payload" );
453 } 454 }
454 455
455 toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); 456 while (!BobCC.incoming)
456 457 iterate_tox(bootstrap, Alice, Bob);
457 /* Wait 2 seconds and change transmission type */ 458
458 if (time(NULL) - times_they_are_a_changin > 2) { 459 {
459 times_they_are_a_changin = time(NULL); 460 TOXAV_ERR_ANSWER rc;
460 muhcaps.audio_bitrate ++; 461 toxav_answer(BobAV, 0, 48, 0, &rc);
461 toxav_change_settings(status_control.Alice.av, status_control.Alice.call_index, &muhcaps); 462
463 if (rc != TOXAV_ERR_ANSWER_OK) {
464 printf("toxav_answer failed: %d\n", rc);
465 ck_assert(0);
466 }
462 } 467 }
463 468
464 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ 469 iterate_tox(bootstrap, Alice, Bob);
465 step++; /* This terminates the loop */ 470
466 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); 471 printf("Call started as audio only\n");
467 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); 472 printf("Turning on video for Alice...\n");
468 473 ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 1000, NULL));
469 /* Call over Alice hangs up */ 474
470 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); 475 iterate_tox(bootstrap, Alice, Bob);
476 ck_assert(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V);
477
478 printf("Turning off video for Alice...\n");
479 ck_assert(toxav_bit_rate_set(AliceAV, 0, -1, 0, NULL));
480
481 iterate_tox(bootstrap, Alice, Bob);
482 ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_V));
483
484 printf("Turning off audio for Alice...\n");
485 ck_assert(toxav_bit_rate_set(AliceAV, 0, 0, -1, NULL));
486
487 iterate_tox(bootstrap, Alice, Bob);
488 ck_assert(!(BobCC.state & TOXAV_FRIEND_CALL_STATE_SENDING_A));
489
490 {
491 TOXAV_ERR_CALL_CONTROL rc;
492 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
493
494 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
495 printf("toxav_call_control failed: %d\n", rc);
496 ck_assert(0);
497 }
471 } 498 }
499
500 iterate_tox(bootstrap, Alice, Bob);
501 ck_assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
502
503 printf("Success!\n");
472 } 504 }
473 TERMINATE_SCOPE() 505
474 506 if (TEST_PAUSE_RESUME_SEND) { /* Stop and resume audio/video payload and test send options */
475 507 printf("\nTrying stop/resume functionality...\n");
476 /************************************************************************************************* 508
477 * Other flows 509 memset(&AliceCC, 0, sizeof(CallControl));
478 */ 510 memset(&BobCC, 0, sizeof(CallControl));
479 511
480 /* 512 /* Assume sending audio and video */
481 * Call and reject 513 {
482 */ 514 TOXAV_ERR_CALL rc;
483 { 515 toxav_call(AliceAV, 0, 48, 0, &rc);
484 int step = 0; 516
485 int running = 1; 517 if (rc != TOXAV_ERR_CALL_OK) {
486 518 printf("toxav_call failed: %d\n", rc);
487 while (running) { 519 ck_assert(0);
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 } 520 }
513
514 c_sleep(20);
515 } 521 }
516 522
517 printf("\n"); 523 while (!BobCC.incoming)
518 } 524 iterate_tox(bootstrap, Alice, Bob);
519 525
520 526 {
521 /* 527 TOXAV_ERR_ANSWER rc;
522 * Call and cancel 528 toxav_answer(BobAV, 0, 48, 0, &rc);
523 */ 529
524 { 530 if (rc != TOXAV_ERR_ANSWER_OK) {
525 int step = 0; 531 printf("toxav_answer failed: %d\n", rc);
526 int running = 1; 532 ck_assert(0);
527
528 while (running) {
529 tox_iterate(bootstrap_node);
530 tox_iterate(Alice);
531 tox_iterate(Bob);
532
533 toxav_do(status_control.Alice.av);
534 toxav_do(status_control.Bob.av);
535
536
537 switch ( step ) {
538 case 0: /* Alice */
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
544
545 case 1: /* Alice again */
546 if (status_control.Bob.status == Ringing) {
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
552 break;
553
554 case 2: /* Wait for Both to have status ended */
555 if (status_control.Bob.status == Canceled) running = 0;
556
557 break;
558 } 533 }
559
560 c_sleep(20);
561 } 534 }
562 535
563 printf("\n"); 536 int16_t PCM[5670];
564 } 537
565 538 iterate_tox(bootstrap, Alice, Bob);
566 /* 539 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL));
567 * Timeout 540 iterate_tox(bootstrap, Alice, Bob);
568 */ 541 ck_assert(!toxav_audio_send_frame(AliceAV, 0, PCM, 960, 1, 48000, NULL));
569 { 542 ck_assert(!toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL));
570 int step = 0; 543 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
571 int running = 1; 544 iterate_tox(bootstrap, Alice, Bob);
572 545 ck_assert(toxav_audio_send_frame(AliceAV, 0, PCM, 960, 1, 48000, NULL));
573 while (running) { 546 ck_assert(toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL));
574 tox_iterate(bootstrap_node); 547 iterate_tox(bootstrap, Alice, Bob);
575 tox_iterate(Alice); 548
576 tox_iterate(Bob); 549 {
577 550 TOXAV_ERR_CALL_CONTROL rc;
578 toxav_do(status_control.Alice.av); 551 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
579 toxav_do(status_control.Bob.av); 552
580 553 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
581 switch ( step ) { 554 printf("toxav_call_control failed: %d\n", rc);
582 case 0: 555 ck_assert(0);
583 printf("Alice is calling...\n");
584 toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10);
585 step++;
586 break;
587
588 case 1:
589 if (status_control.Alice.status == TimedOut) running = 0;
590
591 break;
592 } 556 }
593
594 c_sleep(20);
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);
606 568 tox_kill(Alice);
607 printf("Calls ended!\n"); 569 tox_kill(bootstrap);
570
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/*************************************************************************************************/ 580
617 581 test_AV_flows();
618 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..9819b694 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,318 @@
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
41typedef struct _Party {
42 CallStatus status;
43 ToxAv *av;
44 int id;
45} Party;
46
47typedef struct _ACall {
48 pthread_t tid;
49 int idx;
50 38
51 Party Caller; 39typedef struct {
52 Party Callee; 40 bool incoming;
53} ACall; 41 uint32_t state;
54 42} CallControl;
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{
63 if (length == 7 && memcmp("gentoo", data, 7) == 0) {
64 tox_friend_add_norequest(m, public_key, 0);
65 }
66}
67 43
44typedef struct {
45 ToxAV* AliceAV;
46 ToxAV* BobAV;
47 CallControl* AliceCC;
48 CallControl* BobCC;
49 uint32_t friend_number;
50} thread_data;
68 51
69/******************************************************************************/ 52/**
70void callback_recv_invite ( void *av, int32_t call_index, void *_arg ) 53 * Callbacks
54 */
55void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
71{ 56{
72 Status *cast = _arg; 57 (void) av;
73 cast->calls[call_index].Callee.status = Ringing; 58 (void) audio_enabled;
59 (void) video_enabled;
60
61 printf("Handling CALL callback\n");
62 ((CallControl*)user_data)[friend_number].incoming = true;
74} 63}
75void callback_recv_ringing ( 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)
76{ 65{
77 Status *cast = _arg; 66 printf("Handling CALL STATE callback: %d %p\n", state, av);
78 cast->calls[call_index].Caller.status = Ringing; 67 ((CallControl*)user_data)[friend_number].state = state;
79} 68}
80void callback_call_ended ( 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)
81{ 74{
82 Status *cast = _arg; 75 (void) av;
83 76 (void) friend_number;
84 if (av == cast->calls[call_index].Caller.av) 77 (void) width;
85 cast->calls[call_index].Caller.status = Ended; 78 (void) height;
86 else 79 (void) y;
87 cast->calls[call_index].Callee.status = Ended; 80 (void) u;
88} 81 (void) v;
89void callback_call_started ( void *av, int32_t call_index, void *_arg ) 82 (void) ystride;
90{ 83 (void) ustride;
91 Status *cast = _arg; 84 (void) vstride;
92 85 (void) user_data;
93 if (av == cast->calls[call_index].Caller.av)
94 cast->calls[call_index].Caller.status = InCall;
95 else
96 cast->calls[call_index].Callee.status = InCall;
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 if (length == 7 && memcmp("gentoo", data, 7) == 0) {
105} 106 ck_assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0);
106 107 }
107void callback_requ_timeout ( void *av, int32_t call_index, void *_arg )
108{
109 ck_assert_msg(0, "No answer!");
110} 108}
111 109
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 110
118void register_callbacks(ToxAv *av, void *data) 111/**
112 * Iterate helper
113 */
114ToxAV* setup_av_instance(Tox* tox, CallControl *CC)
119{ 115{
120 toxav_register_callstate_callback(av, callback_call_started, av_OnStart, data); 116 TOXAV_ERR_NEW error;
121 toxav_register_callstate_callback(av, callback_call_canceled, av_OnCancel, data); 117
122 toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data); 118 ToxAV* av = toxav_new(tox, &error);
123 toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data); 119 ck_assert(error == TOXAV_ERR_NEW_OK);
124 toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data); 120
125 toxav_register_callstate_callback(av, callback_recv_ringing, av_OnRinging, data); 121 toxav_callback_call(av, t_toxav_call_cb, CC);
126 toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data); 122 toxav_callback_call_state(av, t_toxav_call_state_cb, CC);
127 123 toxav_callback_video_receive_frame(av, t_toxav_receive_video_frame_cb, CC);
128 124 toxav_callback_audio_receive_frame(av, t_toxav_receive_audio_frame_cb, CC);
129 toxav_register_audio_callback(av, callback_audio, NULL); 125
130 toxav_register_video_callback(av, callback_video, NULL); 126 return av;
131} 127}
132/*************************************************************************************************/ 128void* call_thread(void* pd)
133
134int call_running[3];
135
136void *in_thread_call (void *arg)
137{ 129{
138#define call_print(call, what, args...) printf("[%d] " what "\n", call, ##args) 130 ToxAV* AliceAV = ((thread_data*) pd)->AliceAV;
139 131 ToxAV* BobAV = ((thread_data*) pd)->BobAV;
140 ACall *this_call = arg; 132 CallControl* AliceCC = ((thread_data*) pd)->AliceCC;
141 uint64_t start = 0; 133 CallControl* BobCC = ((thread_data*) pd)->BobCC;
142 int step = 0; 134 uint32_t friend_number = ((thread_data*) pd)->friend_number;
143 int call_idx; 135
144 136
145 const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000); 137 memset(AliceCC, 0, sizeof(CallControl));
146 int16_t sample_payload[frame_size]; 138 memset(BobCC, 0, sizeof(CallControl));
147 randombytes((uint8_t *)sample_payload, sizeof(int16_t) * frame_size); 139
148 140 { /* Call */
149 uint8_t prepared_payload[RTP_PAYLOAD_SIZE]; 141 TOXAV_ERR_CALL rc;
150 142 toxav_call(AliceAV, friend_number, 48, 3000, &rc);
151 register_callbacks(this_call->Caller.av, &status_control); 143
152 register_callbacks(this_call->Callee.av, arg); 144 if (rc != TOXAV_ERR_CALL_OK) {
153 145 printf("toxav_call failed: %d\n", rc);
154 /* NOTE: CALLEE WILL ALWAHYS NEED CALL_IDX == 0 */ 146 ck_assert(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
168 case 1: /* CALLEE */
169 pthread_mutex_lock(&muhmutex);
170
171 if (this_call->Caller.status == Ringing) {
172 call_print(call_idx, "Callee answers ...");
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
180 pthread_mutex_unlock(&muhmutex);
181 break;
182
183 case 2: /* Rtp transmission */
184 pthread_mutex_lock(&muhmutex);
185
186 if (this_call->Caller.status == InCall) { /* I think this is okay */
187 call_print(call_idx, "Sending rtp ...");
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
206 toxav_send_audio(this_call->Callee.av, 0, prepared_payload, payload_size);
207
208 /* Both receive */
209 int16_t storage[RTP_PAYLOAD_SIZE];
210 int recved;
211
212 c_sleep(20);
213 }
214
215 step++; /* This terminates the loop */
216
217 pthread_mutex_lock(&muhmutex);
218 toxav_kill_transmission(this_call->Callee.av, 0);
219 toxav_kill_transmission(this_call->Caller.av, call_idx);
220 pthread_mutex_unlock(&muhmutex);
221
222 /* Call over CALLER hangs up */
223 toxav_hangup(this_call->Caller.av, call_idx);
224 call_print(call_idx, "Hanging up ...");
225
226 pthread_mutex_lock(&muhmutex);
227 }
228
229 pthread_mutex_unlock(&muhmutex);
230 break;
231
232 case 3: /* Wait for Both to have status ended */
233 pthread_mutex_lock(&muhmutex);
234
235 if (this_call->Caller.status == Ended) {
236 pthread_mutex_unlock(&muhmutex);
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
243 pthread_mutex_unlock(&muhmutex);
244
245 break;
246
247 } 147 }
248
249 c_sleep(20);
250
251 pthread_mutex_lock(&muhmutex);
252 } 148 }
253 149
254 pthread_mutex_unlock(&muhmutex); 150 while (!BobCC->incoming)
255 call_print(call_idx, "Call ended successfully!"); 151 c_sleep(10);
152
153 { /* Answer */
154 TOXAV_ERR_ANSWER rc;
155 toxav_answer(BobAV, 0, 8, 500, &rc);
156
157 if (rc != TOXAV_ERR_ANSWER_OK) {
158 printf("toxav_answer failed: %d\n", rc);
159 ck_assert(0);
160 }
161 }
162
163 c_sleep(30);
164
165 int16_t PCM[960];
166 uint8_t video_y[800*600];
167 uint8_t video_u[800*600 / 2];
168 uint8_t video_v[800*600 / 2];
169
170 memset(PCM, 0, sizeof(PCM));
171 memset(video_y, 0, sizeof(video_y));
172 memset(video_u, 0, sizeof(video_u));
173 memset(video_v, 0, sizeof(video_v));
174
175 time_t start_time = time(NULL);
176 while(time(NULL) - start_time < 4) {
177 toxav_iterate(AliceAV);
178 toxav_iterate(BobAV);
179
180 toxav_audio_send_frame(AliceAV, friend_number, PCM, 960, 1, 48000, NULL);
181 toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL);
182
183 toxav_video_send_frame(AliceAV, friend_number, 800, 600, video_y, video_u, video_v, NULL);
184 toxav_video_send_frame(BobAV, 0, 800, 600, video_y, video_u, video_v, NULL);
185
186 c_sleep(10);
187 }
188
189 { /* Hangup */
190 TOXAV_ERR_CALL_CONTROL rc;
191 toxav_call_control(AliceAV, friend_number, TOXAV_CALL_CONTROL_CANCEL, &rc);
192
193 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
194 printf("toxav_call_control failed: %d %p %p\n", rc, AliceAV, BobAV);
195 }
196 }
197
198 c_sleep(30);
199
200 printf ("Closing thread\n");
256 pthread_exit(NULL); 201 pthread_exit(NULL);
257} 202}
258 203
259 204
260
261
262
263START_TEST(test_AV_three_calls) 205START_TEST(test_AV_three_calls)
264// void test_AV_three_calls()
265{ 206{
266 long long unsigned int cur_time = time(NULL); 207 Tox* Alice, *bootstrap, *Bobs[3];
267 Tox *bootstrap_node = tox_new(0, 0); 208 ToxAV* AliceAV, *BobsAV[3];
268 Tox *caller = tox_new(0, 0); 209
269 Tox *callees[3] = { 210 CallControl AliceCC[3], BobsCC[3];
270 tox_new(0, 0), 211
271 tox_new(0, 0), 212 {
272 tox_new(0, 0), 213 TOX_ERR_NEW error;
273 }; 214
274 215 bootstrap = tox_new(NULL, &error);
275 216 ck_assert(error == TOX_ERR_NEW_OK);
276 ck_assert_msg(bootstrap_node != NULL, "Failed to create bootstrap node"); 217
277 218 Alice = tox_new(NULL, &error);
278 int i = 0; 219 ck_assert(error == TOX_ERR_NEW_OK);
279 220
280 for (; i < 3; i ++) { 221 Bobs[0] = tox_new(NULL, &error);
281 ck_assert_msg(callees[i] != NULL, "Failed to create 3 tox instances"); 222 ck_assert(error == TOX_ERR_NEW_OK);
282 } 223
283 224 Bobs[1] = tox_new(NULL, &error);
284 for ( i = 0; i < 3; i ++ ) { 225 ck_assert(error == TOX_ERR_NEW_OK);
285 uint32_t to_compare = 974536; 226
286 tox_callback_friend_request(callees[i], accept_friend_request, &to_compare); 227 Bobs[2] = tox_new(NULL, &error);
287 uint8_t address[TOX_ADDRESS_SIZE]; 228 ck_assert(error == TOX_ERR_NEW_OK);
288 tox_self_get_address(callees[i], address);
289
290 uint32_t test = tox_friend_add(caller, address, (uint8_t *)"gentoo", 7, 0);
291 ck_assert_msg( test == i, "Failed to add friend error code: %i", test);
292 } 229 }
293 230
231 printf("Created 5 instances of Tox\n");
232 printf("Preparing network...\n");
233 long long unsigned int cur_time = time(NULL);
234
235 uint32_t to_compare = 974536;
236 uint8_t address[TOX_ADDRESS_SIZE];
237
238 tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
239 tox_self_get_address(Alice, address);
240
241
242 ck_assert(tox_friend_add(Bobs[0], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
243 ck_assert(tox_friend_add(Bobs[1], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
244 ck_assert(tox_friend_add(Bobs[2], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
245
294 uint8_t off = 1; 246 uint8_t off = 1;
295 247
296 while (1) { 248 while (1) {
297 tox_iterate(bootstrap_node); 249 tox_iterate(bootstrap);
298 tox_iterate(caller); 250 tox_iterate(Alice);
299 251 tox_iterate(Bobs[0]);
300 for (i = 0; i < 3; i ++) { 252 tox_iterate(Bobs[1]);
301 tox_iterate(callees[i]); 253 tox_iterate(Bobs[2]);
302 } 254
303 255 if (tox_self_get_connection_status(bootstrap) &&
304 256 tox_self_get_connection_status(Alice) &&
305 if (tox_self_get_connection_status(bootstrap_node) && 257 tox_self_get_connection_status(Bobs[0]) &&
306 tox_self_get_connection_status(caller) && 258 tox_self_get_connection_status(Bobs[1]) &&
307 tox_self_get_connection_status(callees[0]) && 259 tox_self_get_connection_status(Bobs[2]) && off) {
308 tox_self_get_connection_status(callees[1]) &&
309 tox_self_get_connection_status(callees[2]) && off) {
310 printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time); 260 printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
311 off = 0; 261 off = 0;
312 } 262 }
313 263
314 264 if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
315 if (tox_friend_get_connection_status(caller, 0, 0) && 265 tox_friend_get_connection_status(Alice, 1, NULL) == TOX_CONNECTION_UDP &&
316 tox_friend_get_connection_status(caller, 1, 0) && 266 tox_friend_get_connection_status(Alice, 2, NULL) == TOX_CONNECTION_UDP &&
317 tox_friend_get_connection_status(caller, 2, 0) ) 267 tox_friend_get_connection_status(Bobs[0], 0, NULL) == TOX_CONNECTION_UDP &&
268 tox_friend_get_connection_status(Bobs[1], 0, NULL) == TOX_CONNECTION_UDP &&
269 tox_friend_get_connection_status(Bobs[2], 0, NULL) == TOX_CONNECTION_UDP)
318 break; 270 break;
319 271
320 c_sleep(20); 272 c_sleep(20);
321 } 273 }
322 274
323 printf("All set after %llu seconds! Starting call...\n", time(NULL) - cur_time); 275 AliceAV = setup_av_instance(Alice, AliceCC);
324 276 BobsAV[0] = setup_av_instance(Bobs[0], BobsCC + 0);
325 ToxAv *uniqcallerav = toxav_new(caller, 3); 277 BobsAV[1] = setup_av_instance(Bobs[1], BobsCC + 1);
326 278 BobsAV[2] = setup_av_instance(Bobs[2], BobsCC + 2);
327 for (i = 0; i < 3; i ++) { 279
328 status_control.calls[i].idx = i; 280 printf("Created 4 instances of ToxAV\n");
329 281 printf("All set after %llu seconds!\n", time(NULL) - cur_time);
330 status_control.calls[i].Caller.av = uniqcallerav; 282
331 status_control.calls[i].Caller.id = 0; 283 thread_data tds[3];
332 status_control.calls[i].Caller.status = none; 284 tds[0].AliceAV = AliceAV;
333 285 tds[0].BobAV = BobsAV[0];
334 status_control.calls[i].Callee.av = toxav_new(callees[i], 1); 286 tds[0].AliceCC = AliceCC + 0;
335 status_control.calls[i].Callee.id = i; 287 tds[0].BobCC = BobsCC + 0;
336 status_control.calls[i].Callee.status = none; 288 tds[0].friend_number = 0;
337 } 289
338 290 tds[1].AliceAV = AliceAV;
339 pthread_mutex_init(&muhmutex, NULL); 291 tds[1].BobAV = BobsAV[1];
340 292 tds[1].AliceCC = AliceCC + 1;
341 for ( i = 0; i < 3; i++ ) { 293 tds[1].BobCC = BobsCC + 1;
342 call_running[i] = 1; 294 tds[1].friend_number = 1;
343 pthread_create(&status_control.calls[i].tid, NULL, in_thread_call, &status_control.calls[i]); 295
344 } 296 tds[2].AliceAV = AliceAV;
345 297 tds[2].BobAV = BobsAV[2];
346 /* Now start 3 calls and they'll run for 10 s */ 298 tds[2].AliceCC = AliceCC + 2;
347 299 tds[2].BobCC = BobsCC + 2;
348 for ( i = 0; i < 3; i++ ) 300 tds[2].friend_number = 2;
349 pthread_detach(status_control.calls[i].tid); 301
350 302 pthread_t tids[3];
351 while (call_running[0] || call_running[1] || call_running[2]) { 303 (void) pthread_create(tids + 0, NULL, call_thread, tds + 0);
352 pthread_mutex_lock(&muhmutex); 304 (void) pthread_create(tids + 1, NULL, call_thread, tds + 1);
353 305 (void) pthread_create(tids + 2, NULL, call_thread, tds + 2);
354 tox_iterate(bootstrap_node); 306
355 tox_iterate(caller); 307 (void) pthread_detach(tids[0]);
356 tox_iterate(callees[0]); 308 (void) pthread_detach(tids[1]);
357 tox_iterate(callees[1]); 309 (void) pthread_detach(tids[2]);
358 tox_iterate(callees[2]); 310
359 311 time_t start_time = time(NULL);
360 for ( i = 0; i < 3; i++ ) 312 while (time(NULL) - start_time < 5) {
361 toxav_do(status_control.calls[0].Caller.av); 313 tox_iterate(Alice);
362 314 tox_iterate(Bobs[0]);
363 toxav_do(status_control.calls[0].Callee.av); 315 tox_iterate(Bobs[1]);
364 toxav_do(status_control.calls[1].Callee.av); 316 tox_iterate(Bobs[2]);
365 toxav_do(status_control.calls[2].Callee.av);
366
367 pthread_mutex_unlock(&muhmutex);
368 c_sleep(20); 317 c_sleep(20);
369 } 318 }
370 319
371 toxav_kill(status_control.calls[0].Caller.av); 320 (void) pthread_join(tids[0], NULL);
372 toxav_kill(status_control.calls[0].Callee.av); 321 (void) pthread_join(tids[1], NULL);
373 toxav_kill(status_control.calls[1].Callee.av); 322 (void) pthread_join(tids[2], NULL);
374 toxav_kill(status_control.calls[2].Callee.av); 323
375 324 printf ("Killing all instances\n");
376 tox_kill(bootstrap_node); 325 toxav_kill(BobsAV[0]);
377 tox_kill(caller); 326 toxav_kill(BobsAV[1]);
378 327 toxav_kill(BobsAV[2]);
379 for ( i = 0; i < 3; i ++) 328 toxav_kill(AliceAV);
380 tox_kill(callees[i]); 329 tox_kill(Bobs[0]);
381 330 tox_kill(Bobs[1]);
331 tox_kill(Bobs[2]);
332 tox_kill(Alice);
333 tox_kill(bootstrap);
334
335 printf("\nTest successful!\n");
382} 336}
383END_TEST 337END_TEST
384 338
385 339
386 340#ifndef HAVE_LIBCHECK
387 341int main(int argc, char *argv[])
342{
343 (void) argc;
344 (void) argv;
345
346 test_AV_three_calls();
347 return 0;
348}
349#else
388Suite *tox_suite(void) 350Suite *tox_suite(void)
389{ 351{
390 Suite *s = suite_create("ToxAV"); 352 Suite *s = suite_create("ToxAV");
@@ -399,6 +361,9 @@ Suite *tox_suite(void)
399 361
400int main(int argc, char *argv[]) 362int main(int argc, char *argv[])
401{ 363{
364 (void) argc;
365 (void) argv;
366
402 Suite *tox = tox_suite(); 367 Suite *tox = tox_suite();
403 SRunner *test_runner = srunner_create(tox); 368 SRunner *test_runner = srunner_create(tox);
404 369
@@ -410,8 +375,5 @@ int main(int argc, char *argv[])
410 srunner_free(test_runner); 375 srunner_free(test_runner);
411 376
412 return number_failed; 377 return number_failed;
413
414// test_AV_three_calls();
415
416// return 0;
417} 378}
379#endif
diff --git a/configure.ac b/configure.ac
index 2b7f3a2e..01680ac6 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..fa6a831f
--- /dev/null
+++ b/testing/av_test.c
@@ -0,0 +1,731 @@
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 if (rb_read(cc->arb, (void**)&f)) {
107 pthread_mutex_unlock(cc->arb_mutex);
108 Pa_WriteStream(adout, f->data, f->size);
109 free(f);
110 } else {
111 pthread_mutex_unlock(cc->arb_mutex);
112 c_sleep(10);
113 }
114 }
115}
116
117/**
118 * Callbacks
119 */
120void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
121{
122 printf("Handling CALL callback\n");
123 ((CallControl*)user_data)->incoming = true;
124}
125void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
126{
127 printf("Handling CALL STATE callback: %d\n", state);
128 ((CallControl*)user_data)->state = state;
129}
130void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
131 uint16_t width, uint16_t height,
132 uint8_t const *y, uint8_t const *u, uint8_t const *v,
133 int32_t ystride, int32_t ustride, int32_t vstride,
134 void *user_data)
135{
136 ystride = abs(ystride);
137 ustride = abs(ustride);
138 vstride = abs(vstride);
139
140 uint16_t *img_data = malloc(height * width * 6);
141
142 unsigned long int i, j;
143 for (i = 0; i < height; ++i) {
144 for (j = 0; j < width; ++j) {
145 uint8_t *point = (uint8_t*) img_data + 3 * ((i * width) + j);
146 int yx = y[(i * ystride) + j];
147 int ux = u[((i / 2) * ustride) + (j / 2)];
148 int vx = v[((i / 2) * vstride) + (j / 2)];
149
150 point[0] = YUV2R(yx, ux, vx);
151 point[1] = YUV2G(yx, ux, vx);
152 point[2] = YUV2B(yx, ux, vx);
153 }
154 }
155
156
157 CvMat mat = cvMat(height, width, CV_8UC3, img_data);
158
159 CvSize sz = {.height = height, .width = width};
160
161 IplImage* header = cvCreateImageHeader(sz, 1, 3);
162 IplImage* img = cvGetImage(&mat, header);
163 cvShowImage(vdout, img);
164 free(img_data);
165}
166void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
167 int16_t const *pcm,
168 size_t sample_count,
169 uint8_t channels,
170 uint32_t sampling_rate,
171 void *user_data)
172{
173 CallControl* cc = user_data;
174 frame* f = malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t) * channels);
175 memcpy(f->data, pcm, sample_count * sizeof(int16_t) * channels);
176 f->size = sample_count;
177
178 pthread_mutex_lock(cc->arb_mutex);
179 free(rb_write(cc->arb, f));
180 pthread_mutex_unlock(cc->arb_mutex);
181}
182void t_toxav_bit_rate_status_cb(ToxAV *av, uint32_t friend_number,
183 uint32_t audio_bit_rate, uint32_t video_bit_rate,
184 void *user_data)
185{
186 printf ("Suggested bit rates: audio: %d video: %d\n", audio_bit_rate, video_bit_rate);
187}
188void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
189{
190 if (length == 7 && memcmp("gentoo", data, 7) == 0) {
191 assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0);
192 }
193}
194
195/**
196 */
197void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxAV** BobAV, CallControl* BobCC)
198{
199 Tox* Alice;
200 Tox* Bob;
201
202 struct Tox_Options opts;
203 tox_options_default(&opts);
204
205 opts.end_port = 0;
206 opts.ipv6_enabled = false;
207
208 {
209 TOX_ERR_NEW error;
210
211 opts.start_port = 33445;
212 *bootstrap = tox_new(&opts, &error);
213 assert(error == TOX_ERR_NEW_OK);
214
215 opts.start_port = 33455;
216 Alice = tox_new(&opts, &error);
217 assert(error == TOX_ERR_NEW_OK);
218
219 opts.start_port = 33465;
220 Bob = tox_new(&opts, &error);
221 assert(error == TOX_ERR_NEW_OK);
222 }
223
224 printf("Created 3 instances of Tox\n");
225 printf("Preparing network...\n");
226 long long unsigned int cur_time = time(NULL);
227
228 uint32_t to_compare = 974536;
229 uint8_t address[TOX_ADDRESS_SIZE];
230
231 tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
232 tox_self_get_address(Alice, address);
233
234
235 assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
236
237 uint8_t off = 1;
238
239 while (1) {
240 tox_iterate(*bootstrap);
241 tox_iterate(Alice);
242 tox_iterate(Bob);
243
244 if (tox_self_get_connection_status(*bootstrap) &&
245 tox_self_get_connection_status(Alice) &&
246 tox_self_get_connection_status(Bob) && off) {
247 printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
248 off = 0;
249 }
250
251 if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
252 tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP)
253 break;
254
255 c_sleep(20);
256 }
257
258
259 TOXAV_ERR_NEW rc;
260 *AliceAV = toxav_new(Alice, &rc);
261 assert(rc == TOXAV_ERR_NEW_OK);
262
263 *BobAV = toxav_new(Bob, &rc);
264 assert(rc == TOXAV_ERR_NEW_OK);
265
266
267 /* Alice */
268 toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC);
269 toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC);
270 toxav_callback_bit_rate_status(*AliceAV, t_toxav_bit_rate_status_cb, AliceCC);
271 toxav_callback_video_receive_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC);
272 toxav_callback_audio_receive_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC);
273
274 /* Bob */
275 toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC);
276 toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC);
277 toxav_callback_bit_rate_status(*BobAV, t_toxav_bit_rate_status_cb, BobCC);
278 toxav_callback_video_receive_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC);
279 toxav_callback_audio_receive_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC);
280
281
282 printf("Created 2 instances of ToxAV\n");
283 printf("All set after %llu seconds!\n", time(NULL) - cur_time);
284}
285int iterate_tox(Tox* bootstrap, ToxAV* AliceAV, ToxAV* BobAV)
286{
287 tox_iterate(bootstrap);
288 tox_iterate(toxav_get_tox(AliceAV));
289 tox_iterate(toxav_get_tox(BobAV));
290
291 return MIN(tox_iteration_interval(toxav_get_tox(AliceAV)), tox_iteration_interval(toxav_get_tox(BobAV)));
292}
293void* iterate_toxav (void * data)
294{
295 struct toxav_thread_data* data_cast = data;
296#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
297 cvNamedWindow(vdout, CV_WINDOW_AUTOSIZE);
298#endif
299
300 while (data_cast->sig == 0) {
301 toxav_iterate(data_cast->AliceAV);
302 toxav_iterate(data_cast->BobAV);
303 int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV));
304
305 printf("\rIteration interval: %d ", rc);
306 fflush(stdout);
307
308#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
309 if (!rc)
310 rc = 1;
311
312 cvWaitKey(rc);
313#else
314 c_sleep(rc);
315#endif
316 }
317
318 data_cast->sig = 1;
319
320#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
321 cvDestroyWindow(vdout);
322#endif
323
324 pthread_exit(NULL);
325}
326
327int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img)
328{
329 int32_t strides[3] = { 1280, 640, 640 };
330 uint8_t* planes[3] = {
331 malloc(img->height * img->width),
332 malloc(img->height * img->width / 4),
333 malloc(img->height * img->width / 4),
334 };
335
336 int x_chroma_shift = 1;
337 int y_chroma_shift = 1;
338
339 int x, y;
340 for (y = 0; y < img->height; ++y) {
341 for (x = 0; x < img->width; ++x) {
342 uint8_t r = img->imageData[(x + y * img->width) * 3 + 0];
343 uint8_t g = img->imageData[(x + y * img->width) * 3 + 1];
344 uint8_t b = img->imageData[(x + y * img->width) * 3 + 2];
345
346 planes[0][x + y * strides[0]] = RGB2Y(r, g, b);
347 if (!(x % (1 << x_chroma_shift)) && !(y % (1 << y_chroma_shift))) {
348 const int i = x / (1 << x_chroma_shift);
349 const int j = y / (1 << y_chroma_shift);
350 planes[1][i + j * strides[1]] = RGB2U(r, g, b);
351 planes[2][i + j * strides[2]] = RGB2V(r, g, b);
352 }
353 }
354 }
355
356 int rc = toxav_video_send_frame(av, friend_number, img->width, img->height,
357 planes[0], planes[1], planes[2], NULL);
358 free(planes[0]);
359 free(planes[1]);
360 free(planes[2]);
361 return rc;
362}
363int print_audio_devices()
364{
365 int i = 0;
366 for (i = 0; i < Pa_GetDeviceCount(); ++i) {
367 const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
368 if (info)
369 printf("%d) %s\n", i, info->name);
370 }
371
372 return 0;
373}
374int print_help (const char* name)
375{
376 printf("Usage: %s -[a:v:o:dh]\n"
377 "-a <path> audio input file\n"
378 "-b <ms> audio frame duration\n"
379 "-v <path> video input file\n"
380 "-x <ms> video frame duration\n"
381 "-o <idx> output audio device index\n"
382 "-d print output audio devices\n"
383 "-h print this help\n", name);
384
385 return 0;
386}
387
388int main (int argc, char** argv)
389{
390 freopen("/dev/zero", "w", stderr);
391 Pa_Initialize();
392
393 struct stat st;
394
395 /* AV files for testing */
396 const char* af_name = NULL;
397 const char* vf_name = NULL;
398 long audio_out_dev_idx = -1;
399
400 int32_t audio_frame_duration = 20;
401 int32_t video_frame_duration = 10;
402
403 /* Parse settings */
404 CHECK_ARG: switch (getopt(argc, argv, "a:b:v:x:o:dh")) {
405 case 'a':
406 af_name = optarg;
407 goto CHECK_ARG;
408 case 'b':{
409 char *d;
410 audio_frame_duration = strtol(optarg, &d, 10);
411 if (*d) {
412 printf("Invalid value for argument: 'b'");
413 exit(1);
414 }
415 goto CHECK_ARG;
416 }
417 case 'v':
418 vf_name = optarg;
419 goto CHECK_ARG;
420 case 'x':{
421 char *d;
422 video_frame_duration = strtol(optarg, &d, 10);
423 if (*d) {
424 printf("Invalid value for argument: 'x'");
425 exit(1);
426 }
427 goto CHECK_ARG;
428 }
429 case 'o': {
430 char *d;
431 audio_out_dev_idx = strtol(optarg, &d, 10);
432 if (*d) {
433 printf("Invalid value for argument: 'o'");
434 exit(1);
435 }
436 goto CHECK_ARG;
437 }
438 case 'd':
439 return print_audio_devices();
440 case 'h':
441 return print_help(argv[0]);
442 case '?':
443 exit(1);
444 case -1:;
445 }
446
447 { /* Check files */
448 if (!af_name) {
449 printf("Required audio input file!\n");
450 exit(1);
451 }
452
453 if (!vf_name) {
454 printf("Required video input file!\n");
455 exit(1);
456 }
457
458 /* Check for files */
459 if(stat(af_name, &st) != 0 || !S_ISREG(st.st_mode))
460 {
461 printf("%s doesn't seem to be a regular file!\n", af_name);
462 exit(1);
463 }
464
465 if(stat(vf_name, &st) != 0 || !S_ISREG(st.st_mode))
466 {
467 printf("%s doesn't seem to be a regular file!\n", vf_name);
468 exit(1);
469 }
470 }
471
472 if (audio_out_dev_idx < 0)
473 audio_out_dev_idx = Pa_GetDefaultOutputDevice();
474
475 const PaDeviceInfo* audio_dev = Pa_GetDeviceInfo(audio_out_dev_idx);
476 if (!audio_dev) {
477 fprintf(stderr, "Device under index: %ld invalid", audio_out_dev_idx);
478 return 1;
479 }
480
481 printf("Using audio device: %s\n", audio_dev->name);
482 printf("Using audio file: %s\n", af_name);
483 printf("Using video file: %s\n", vf_name);
484
485 /* START TOX NETWORK */
486
487 Tox *bootstrap;
488 ToxAV *AliceAV;
489 ToxAV *BobAV;
490
491 CallControl AliceCC;
492 CallControl BobCC;
493
494 initialize_tox(&bootstrap, &AliceAV, &AliceCC, &BobAV, &BobCC);
495
496 if (TEST_TRANSFER_A) {
497 SNDFILE* af_handle;
498 SF_INFO af_info;
499
500 printf("\nTrying audio enc/dec...\n");
501
502 memset(&AliceCC, 0, sizeof(CallControl));
503 memset(&BobCC, 0, sizeof(CallControl));
504
505 pthread_mutex_init(AliceCC.arb_mutex, NULL);
506 pthread_mutex_init(BobCC.arb_mutex, NULL);
507
508 AliceCC.arb = rb_new(16);
509 BobCC.arb = rb_new(16);
510
511 { /* Call */
512 TOXAV_ERR_CALL rc;
513 toxav_call(AliceAV, 0, 48, 0, &rc);
514
515 if (rc != TOXAV_ERR_CALL_OK) {
516 printf("toxav_call failed: %d\n", rc);
517 exit(1);
518 }
519 }
520
521 while (!BobCC.incoming)
522 iterate_tox(bootstrap, AliceAV, BobAV);
523
524 { /* Answer */
525 TOXAV_ERR_ANSWER rc;
526 toxav_answer(BobAV, 0, 48, 0, &rc);
527
528 if (rc != TOXAV_ERR_ANSWER_OK) {
529 printf("toxav_answer failed: %d\n", rc);
530 exit(1);
531 }
532 }
533
534 while (AliceCC.state == 0)
535 iterate_tox(bootstrap, AliceAV, BobAV);
536
537 /* Open audio file */
538 af_handle = sf_open(af_name, SFM_READ, &af_info);
539 if (af_handle == NULL) {
540 printf("Failed to open the file.\n");
541 exit(1);
542 }
543
544 int16_t PCM[5760];
545
546 time_t start_time = time(NULL);
547 time_t expected_time = af_info.frames / af_info.samplerate + 2;
548
549
550 /* Start decode thread */
551 struct toxav_thread_data data = {
552 .AliceAV = AliceAV,
553 .BobAV = BobAV,
554 .sig = 0
555 };
556
557 pthread_t dect;
558 pthread_create(&dect, NULL, iterate_toxav, &data);
559 pthread_detach(dect);
560
561 int frame_size = (af_info.samplerate * audio_frame_duration / 1000) * af_info.channels;
562
563 struct PaStreamParameters output;
564 output.device = audio_out_dev_idx;
565 output.channelCount = af_info.channels;
566 output.sampleFormat = paInt16;
567 output.suggestedLatency = audio_dev->defaultHighOutputLatency;
568 output.hostApiSpecificStreamInfo = NULL;
569
570 PaError err = Pa_OpenStream(&adout, NULL, &output, af_info.samplerate, frame_size, paNoFlag, NULL, NULL);
571 assert(err == paNoError);
572
573 err = Pa_StartStream(adout);
574 assert(err == paNoError);
575
576// toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL);
577
578 /* Start write thread */
579 pthread_t t;
580 pthread_create(&t, NULL, pa_write_thread, &BobCC);
581 pthread_detach(t);
582
583 printf("Sample rate %d\n", af_info.samplerate);
584 while (start_time + expected_time > time(NULL) ) {
585 uint64_t enc_start_time = current_time_monotonic();
586 int64_t count = sf_read_short(af_handle, PCM, frame_size);
587 if (count > 0) {
588 TOXAV_ERR_SEND_FRAME rc;
589 if (toxav_audio_send_frame(AliceAV, 0, PCM, count/af_info.channels, af_info.channels, af_info.samplerate, &rc) == false) {
590 printf("Error sending frame of size %ld: %d\n", count, rc);
591 }
592 }
593 iterate_tox(bootstrap, AliceAV, BobAV);
594 c_sleep(abs(audio_frame_duration - (current_time_monotonic() - enc_start_time) - 1));
595 }
596
597 printf("Played file in: %lu; stopping stream...\n", time(NULL) - start_time);
598
599 Pa_StopStream(adout);
600 sf_close(af_handle);
601
602 { /* Hangup */
603 TOXAV_ERR_CALL_CONTROL rc;
604 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
605
606 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
607 printf("toxav_call_control failed: %d\n", rc);
608 exit(1);
609 }
610 }
611
612 iterate_tox(bootstrap, AliceAV, BobAV);
613 assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
614
615 /* Stop decode thread */
616 data.sig = -1;
617 while(data.sig != 1)
618 pthread_yield();
619
620 pthread_mutex_destroy(AliceCC.arb_mutex);
621 pthread_mutex_destroy(BobCC.arb_mutex);
622
623 void* f = NULL;
624 while(rb_read(AliceCC.arb, &f))
625 free(f);
626
627 while(rb_read(BobCC.arb, &f))
628 free(f);
629
630 printf("Success!");
631 }
632
633 if (TEST_TRANSFER_V) {
634 printf("\nTrying video enc/dec...\n");
635
636 memset(&AliceCC, 0, sizeof(CallControl));
637 memset(&BobCC, 0, sizeof(CallControl));
638
639 { /* Call */
640 TOXAV_ERR_CALL rc;
641 toxav_call(AliceAV, 0, 0, 2000, &rc);
642
643 if (rc != TOXAV_ERR_CALL_OK) {
644 printf("toxav_call failed: %d\n", rc);
645 exit(1);
646 }
647 }
648
649 while (!BobCC.incoming)
650 iterate_tox(bootstrap, AliceAV, BobAV);
651
652 { /* Answer */
653 TOXAV_ERR_ANSWER rc;
654 toxav_answer(BobAV, 0, 0, 5000, &rc);
655
656 if (rc != TOXAV_ERR_ANSWER_OK) {
657 printf("toxav_answer failed: %d\n", rc);
658 exit(1);
659 }
660 }
661
662 iterate_tox(bootstrap, AliceAV, BobAV);
663
664 /* Start decode thread */
665 struct toxav_thread_data data = {
666 .AliceAV = AliceAV,
667 .BobAV = BobAV,
668 .sig = 0
669 };
670
671 pthread_t dect;
672 pthread_create(&dect, NULL, iterate_toxav, &data);
673 pthread_detach(dect);
674
675 CvCapture* capture = cvCreateFileCapture(vf_name);
676 if (!capture) {
677 printf("Failed to open video file: %s\n", vf_name);
678 exit(1);
679 }
680
681// toxav_video_bit_rate_set(AliceAV, 0, 5000, false, NULL);
682
683 time_t start_time = time(NULL);
684 while(start_time + 90 > time(NULL)) {
685 IplImage* frame = cvQueryFrame(capture );
686 if (!frame)
687 break;
688
689 send_opencv_img(AliceAV, 0, frame);
690 iterate_tox(bootstrap, AliceAV, BobAV);
691 c_sleep(10);
692 }
693
694 cvReleaseCapture(&capture);
695
696 { /* Hangup */
697 TOXAV_ERR_CALL_CONTROL rc;
698 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
699
700 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
701 printf("toxav_call_control failed: %d\n", rc);
702 exit(1);
703 }
704 }
705
706 iterate_tox(bootstrap, AliceAV, BobAV);
707 assert(BobCC.state == TOXAV_FRIEND_CALL_STATE_FINISHED);
708
709 /* Stop decode thread */
710 printf("Stopping decode thread\n");
711 data.sig = -1;
712 while(data.sig != 1)
713 pthread_yield();
714
715 printf("Success!");
716 }
717
718
719 Tox* Alice = toxav_get_tox(AliceAV);
720 Tox* Bob = toxav_get_tox(BobAV);
721 toxav_kill(BobAV);
722 toxav_kill(AliceAV);
723 tox_kill(Bob);
724 tox_kill(Alice);
725 tox_kill(bootstrap);
726
727 printf("\nTest successful!\n");
728
729 Pa_Terminate();
730 return 0;
731}
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..4f9d3562
--- /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..2530e7fd
--- /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 for (; i < BWC_AVG_PKT_COUNT; i ++)
81 rb_write(retu->rcvpkt.rb, retu->rcvpkt.rb_s + i);
82
83 m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu);
84
85 return retu;
86}
87void bwc_kill(BWControler *bwc)
88{
89 if (!bwc)
90 return;
91
92 m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, NULL, NULL);
93
94 rb_kill(bwc->rcvpkt.rb);
95 free(bwc);
96}
97void bwc_feed_avg(BWControler* bwc, uint32_t bytes)
98{
99 uint32_t *p;
100
101 rb_read(bwc->rcvpkt.rb, (void**) &p);
102 rb_write(bwc->rcvpkt.rb, p);
103
104 *p = bytes;
105}
106void bwc_add_lost(BWControler *bwc, uint32_t bytes)
107{
108 if (!bwc)
109 return;
110
111 if (!bytes) {
112 uint32_t* t_avg[BWC_AVG_PKT_COUNT], c = 1;
113
114 rb_data(bwc->rcvpkt.rb, (void**) t_avg);
115
116 int i = 0;
117 for (; i < BWC_AVG_PKT_COUNT; i ++) {
118 bytes += *(t_avg[i]);
119
120 if (*(t_avg[i]))
121 c++;
122 }
123
124 bytes /= c;
125 }
126
127 bwc->cycle.lost += bytes;
128 send_update(bwc);
129}
130void bwc_add_recv(BWControler *bwc, uint32_t bytes)
131{
132 if (!bwc || !bytes)
133 return;
134
135 bwc->cycle.recv += bytes;
136 send_update(bwc);
137}
138
139
140struct BWCMessage {
141 uint32_t lost;
142 uint32_t recv;
143};
144
145void send_update(BWControler *bwc)
146{
147 if (current_time_monotonic() - bwc->cycle.lfu > BWC_REFRESH_INTERVAL_MS) {
148
149 bwc->cycle.lost /= 10;
150 bwc->cycle.recv /= 10;
151 bwc->cycle.lfu = current_time_monotonic();
152 }
153 else if (current_time_monotonic() - bwc->cycle.lsu > BWC_SEND_INTERVAL_MS) {
154
155 if (bwc->cycle.lost)
156 {
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..7ad39a54 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,794 @@
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);
83int send_error (Messenger *m, uint32_t friend_number, MSIError error);
84static int invoke_callback(MSICall *call, MSICallbackID cb);
85static MSICall *get_call (MSISession *session, uint32_t friend_number);
86MSICall *new_call (MSISession *session, uint32_t friend_number);
87void kill_call (MSICall *call);
88void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data);
89void handle_init (MSICall *call, const MSIMessage *msg);
90void handle_push (MSICall *call, const MSIMessage *msg);
91void handle_pop (MSICall *call, const MSIMessage *msg);
92void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object);
116 93
117 s->callbacks[i].first( s->agent_handler, c, s->callbacks[i].second );
118 }
119}
120 94
121/** 95/**
122 * Parse raw 'data' received from socket into MSIMessage struct. 96 * Public functions
123 * Every message has to have end value of 'end_byte' or _undefined_ behavior
124 * occures. The best practice is to check the end of the message at the handle_packet.
125 */ 97 */
126static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length ) 98void msi_register_callback (MSISession *session, msi_action_cb *callback, MSICallbackID id)
127{ 99{
128 100 if (!session)
129#define FAIL_CONSTRAINT(constraint, wanted) if ((constraint -= wanted) < 1) { LOGGER_ERROR("Read over length!"); return -1; } 101 return;
130#define FAIL_SIZE(byte, valid) if ( byte != valid ) { LOGGER_ERROR("Invalid data size!"); return -1; } 102
131#define FAIL_LIMITS(byte, high) if ( byte > high ) { LOGGER_ERROR("Failed limit!"); return -1; } 103 pthread_mutex_lock(session->mutex);
132 104 session->callbacks[id] = callback;
133 if ( msg == NULL ) { 105 pthread_mutex_unlock(session->mutex);
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
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} 106}
201 107MSISession *msi_new (Messenger *m)
202/**
203 * Create the message.
204 */
205MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value )
206{ 108{
207 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); 109 if (m == NULL) {
208 110 LOGGER_ERROR("Could not init session on empty messenger!");
209 if ( retu == NULL ) {
210 LOGGER_WARNING("Allocation failed! Program might misbehave!");
211 return NULL; 111 return NULL;
212 } 112 }
213 113
214 if ( type == TypeRequest ) { 114 MSISession *retu = calloc (sizeof (MSISession), 1);
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
223 return retu;
224}
225 115
226 116 if (retu == NULL) {
227/** 117 LOGGER_ERROR("Allocation failed! Program might misbehave!");
228 * Parse data from handle_packet.
229 */
230MSIMessage *parse_recv ( const uint8_t *data, uint16_t length )
231{
232 if ( data == NULL ) {
233 LOGGER_WARNING("Tried to parse empty message!");
234 return NULL; 118 return NULL;
235 } 119 }
236 120
237 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); 121 if (create_recursive_mutex(retu->mutex) != 0) {
238 122 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
239 if ( retu == NULL ) { 123 free(retu);
240 LOGGER_WARNING("Allocation failed! Program might misbehave!");
241 return NULL; 124 return NULL;
242 } 125 }
243 126
244 if ( parse_raw_data ( retu, data, length ) == -1 ) { 127 retu->messenger = m;
245 128
246 free ( retu ); 129 m_callback_msi_packet(m, handle_msi_packet, retu);
247 return NULL;
248 }
249 130
131 /* This is called when remote terminates session */
132 m_callback_connectionstatus_internal_av(m, on_peer_status, retu);
133
134 LOGGER_DEBUG("New msi session: %p ", retu);
250 return retu; 135 return retu;
251} 136}
252 137int msi_kill (MSISession *session)
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{ 138{
263 if ( dest == NULL ) { 139 if (session == NULL) {
264 LOGGER_ERROR("No destination space!"); 140 LOGGER_ERROR("Tried to terminate non-existing session");
265 return NULL; 141 return -1;
266 } 142 }
267 143
268 if (value == NULL || value_len == 0) { 144 m_callback_msi_packet((struct Messenger *) session->messenger, NULL, NULL);
269 LOGGER_ERROR("Empty header value"); 145
270 return NULL; 146 if (pthread_mutex_trylock(session->mutex) != 0) {
147 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
148 return -1;
271 } 149 }
272 150
273 *dest = id; 151 if (session->calls) {
274 dest ++; 152 MSIMessage msg;
275 *dest = value_len; 153 msg_init(&msg, requ_pop);
276 dest ++;
277
278 memcpy(dest, value, value_len);
279
280 *length += (2 + value_len);
281
282 return dest + value_len; /* Set to next position ready to be written */
283}
284 154
155 MSICall *it = get_call(session, session->calls_head);
285 156
286/** 157 for (; it; it = it->next) {
287 * Parse MSIMessage to send. 158 send_message(session->messenger, it->friend_number, &msg);
288 */ 159 kill_call(it); /* This will eventually free session->calls */
289uint16_t parse_send ( MSIMessage *msg, uint8_t *dest ) 160 }
290{
291 if (msg == NULL) {
292 LOGGER_ERROR("No message!");
293 return 0;
294 }
295
296 if (dest == NULL ) {
297 LOGGER_ERROR("No destination!");
298 return 0;
299 } 161 }
300 162
301 uint8_t *it = dest; 163 pthread_mutex_unlock(session->mutex);
302 uint16_t size = 0; 164 pthread_mutex_destroy(session->mutex);
303 165
304 if (msg->request.exists) { 166 LOGGER_DEBUG("Terminated session: %p", session);
305 uint8_t cast = msg->request.value; 167 free (session);
306 it = format_output(it, IDRequest, &cast, 1, &size); 168 return 0;
307 } 169}
170int msi_invite (MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
171{
172 if (!session)
173 return -1;
174
175 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number);
308 176
309 if (msg->response.exists) { 177 if (pthread_mutex_trylock(session->mutex) != 0) {
310 uint8_t cast = msg->response.value; 178 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
311 it = format_output(it, IDResponse, &cast, 1, &size); 179 return -1;
312 } 180 }
313 181
314 if (msg->callid.exists) { 182 if (get_call(session, friend_number) != NULL) {
315 it = format_output(it, IDCallId, &msg->callid.value, sizeof(msg->callid.value), &size); 183 LOGGER_ERROR("Already in a call");
184 pthread_mutex_unlock(session->mutex);
185 return -1;
316 } 186 }
317 187
318 if (msg->reason.exists) { 188 (*call) = new_call (session, friend_number);
319 it = format_output(it, IDReason, &msg->reason.value, sizeof(msg->reason.value), &size);
320 }
321 189
322 if (msg->csettings.exists) { 190 if (*call == NULL) {
323 it = format_output(it, IDCSettings, &msg->csettings.value, sizeof(msg->csettings.value), &size); 191 pthread_mutex_unlock(session->mutex);
192 return -1;
324 } 193 }
325 194
326 *it = 0; 195 (*call)->self_capabilities = capabilities;
327 size ++;
328
329 return size;
330}
331
332void msi_msg_set_reason ( MSIMessage *msg, const MSIReasonStrType value )
333{
334 if ( !msg ) return;
335
336 msg->reason.exists = 1;
337 memcpy(msg->reason.value, value, sizeof(MSIReasonStrType));
338}
339
340void msi_msg_set_callid ( MSIMessage *msg, const MSICallIDType value )
341{
342 if ( !msg ) return;
343
344 msg->callid.exists = 1;
345 memcpy(msg->callid.value, value, sizeof(MSICallIDType));
346}
347
348void msi_msg_set_csettings ( MSIMessage *msg, const MSICSettings *value )
349{
350 if ( !msg ) return;
351
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
392void msi_msg_get_csettings ( MSIMessage *msg, MSICSettings *dest )
393{
394 if ( !msg || !dest || !msg->csettings.exists ) return;
395
396 dest->call_type = msg->csettings.value[0];
397 uint8_t *iter = msg->csettings.value + 1;
398
399 memcpy(&dest->video_bitrate, iter, 4);
400 iter += 4;
401 dest->video_bitrate = ntohl(dest->video_bitrate);
402
403 memcpy(&dest->max_video_width, iter, 2);
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
451 for (; i < timer_handler->max_capacity && timer_handler->timers[i]; i ++);
452 217
453 if (i == timer_handler->max_capacity) { 218 MSISession *session = call->session;
454 LOGGER_WARNING("Maximum capacity reached!"); 219
220 if (pthread_mutex_trylock(session->mutex) != 0) {
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)
492
493 uint32_t i;
494 int rc = -1;
495
496 for (i = 0; i < timers_container->max_capacity; ++i) {
497 if (timed_events[i] && timed_events[i]->id == id) {
498 rc = i;
499 break;
500 }
501 }
502
503 if (rc == -1) {
504 LOGGER_WARNING("No event with id: %d", id);
505 return -1; 243 return -1;
506 }
507 244
508 free(timed_events[rc]); 245 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_number);
509 246
510 timed_events[rc] = NULL; 247 MSISession *session = call->session;
511 248
512 i = rc + 1; 249 if (pthread_mutex_trylock(session->mutex) != 0) {
513 250 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
514 for (; i < timers_container->max_capacity && timed_events[i]; i ++) { 251 return -1;
515 timed_events[i - 1] = timed_events[i];
516 timed_events[i] = NULL;
517 }
518
519 timers_container->size--;
520
521 LOGGER_DEBUG("Popped id: %d, current size: %ull ", id, timers_container->size);
522 return 0;
523}
524
525/**
526 * Generate _random_ alphanumerical string.
527 */
528static void t_randomstr ( uint8_t *str, uint32_t size )
529{
530 if (str == NULL) {
531 LOGGER_DEBUG("Empty destination!");
532 return;
533 } 252 }
534 253
535 static const uint8_t _bytes[] = 254 if (call->state != msi_CallRequested) {
536 "0123456789" 255 /* Though sending in invalid state will not cause anything wierd
537 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 256 * Its better to not do it like a maniac */
538 "abcdefghijklmnopqrstuvwxyz"; 257 LOGGER_ERROR("Call is in invalid state!");
539 258 pthread_mutex_unlock(session->mutex);
540 uint32_t _it = 0; 259 return -1;
541
542 for ( ; _it < size; _it++ ) {
543 str[_it] = _bytes[ random_int() % 61 ];
544 } 260 }
545}
546 261
547/* TODO: it would be nice to actually have some sane error codes */ 262 call->self_capabilities = capabilities;
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 263
553 error_no_callid, /* not having call id */ 264 MSIMessage msg;
554 error_no_call, /* no call in session */ 265 msg_init(&msg, requ_push);
555 error_no_crypto_key, /* no crypto key */
556 266
557 error_busy 267 msg.capabilities.exists = true;
268 msg.capabilities.value = capabilities;
558 269
559} MSICallError; /* Error codes */ 270 send_message (session->messenger, call->friend_number, &msg);
560 271
272 call->state = msi_CallActive;
273 pthread_mutex_unlock(session->mutex);
561 274
562/** 275 return 0;
563 * Stringify error code.
564 */
565static const uint8_t *stringify_error ( MSICallError error_code )
566{
567 static const uint8_t *strings[] = {
568 ( uint8_t *) "",
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} 276}
579 277int msi_change_capabilities(MSICall *call, uint8_t capabilities)
580static int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t to )
581{ 278{
582 msi_msg_set_callid ( msg, call->id ); 279 if (!call || !call->session)
280 return -1;
281
282 LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number);
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 324
677 if (!peers) { 325#define CHECK_SIZE(bytes, constraint, size) \
678 LOGGER_WARNING("Allocation failed! Program might misbehave!"); 326 if ((constraint -= (2 + size)) < 1) { LOGGER_ERROR("Read over length!"); return -1; } \
679 return; 327 if (bytes[1] != size) { LOGGER_ERROR("Invalid data size!"); return -1; }
680 }
681 328
682 call->peer_count ++; 329#define CHECK_ENUM_HIGH(bytes, enum_high) /* Assumes size == 1 */ \
683 call->peers = peers; 330 if (bytes[2] > enum_high) { LOGGER_ERROR("Failed enum high limit!"); return -1; }
684 call->peers[call->peer_count - 1] = peer_id;
685
686 LOGGER_DEBUG("Added peer: %d", peer_id);
687}
688 331
332#define SET_UINT8(bytes, header) do { \
333 header.value = bytes[2]; \
334 header.exists = true; \
335 bytes += 3; \
336 } while(0)
689 337
690static MSICall *find_call ( MSISession *session, uint8_t *call_id ) 338#define SET_UINT16(bytes, header) do { \
691{ 339 memcpy(&header.value, bytes + 2, 2);\
692 if ( call_id == NULL ) return NULL; 340 header.exists = true; \
341 bytes += 4; \
342 } while(0)
693 343
694 int32_t i = 0;
695 344
696 for (; i < session->max_calls; i ++ ) 345 assert(dest);
697 if ( session->calls[i] && memcmp(session->calls[i]->id, call_id, sizeof(session->calls[i]->id)) == 0 ) {
698 return session->calls[i];
699 }
700 346
701 return NULL; 347 if (length == 0 || data[length - 1]) { /* End byte must have value 0 */
702} 348 LOGGER_ERROR("Invalid end byte");
703 349 return -1;
704static MSICall *init_call ( MSISession *session, int peers, int ringing_timeout )
705{
706
707 if (peers == 0) {
708 LOGGER_ERROR("No peers!");
709 return NULL;
710 } 350 }
711 351
712 int32_t call_idx = 0; 352 memset(dest, 0, sizeof(*dest));
713
714 for (; call_idx < session->max_calls; call_idx ++) {
715 if ( !session->calls[call_idx] ) {
716
717 if (!(session->calls[call_idx] = calloc ( sizeof ( MSICall ), 1 ))) {
718 LOGGER_WARNING("Allocation failed! Program might misbehave!");
719 return NULL;
720 }
721 353
722 break; 354 const uint8_t *it = data;
723 } 355 int size_constraint = length;
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
776static void handle_remote_connection_change(Messenger *messenger, uint32_t friend_num, uint8_t status, void *session_p)
777{
778 (void)messenger;
779 MSISession *session = session_p;
780 389
781 switch ( status ) { 390#undef CHECK_SIZE
782 case 0: { /* Went offline */ 391#undef CHECK_ENUM_HIGH
783 int32_t j = 0; 392#undef SET_UINT8
784 393#undef SET_UINT16
785 for ( ; j < session->max_calls; j ++ ) {
786
787 if ( !session->calls[j] ) continue;
788
789 uint16_t i = 0;
790
791 for ( ; i < session->calls[j]->peer_count; i ++ )
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
802 default:
803 break;
804 }
805} 394}
806 395uint8_t *msg_parse_header_out (MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length)
807/**
808 * Function called at request timeout
809 */
810static void handle_timeout ( Timer *timer )
811{ 396{
812 /* TODO: Cancel might not arrive there; set up 397 /* Parse a single header for sending */
813 * timers on these cancels and terminate call on 398 assert(dest);
814 * their timeout 399 assert(value);
815 */ 400 assert(value_len);
816 MSICall *call = timer->session->calls[timer->call_idx];
817
818 401
819 if (call) { 402 *dest = id;
820 LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx); 403 dest ++;
404 *dest = value_len;
405 dest ++;
821 406
822 invoke_callback(timer->session, timer->call_idx, msi_OnRequestTimeout); 407 memcpy(dest, value, value_len);
823 msi_cancel(timer->session, timer->call_idx, call->peers [0], "Request timed out");
824 }
825}
826 408
409 *length += (2 + value_len);
827 410
828/********** Request handlers **********/ 411 return dest + value_len; /* Set to next position ready to be written */
829static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *msg ) 412}
413int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg)
830{ 414{
831 LOGGER_DEBUG("Session: %p Handling 'invite' on call: %d", session, call ? call->call_idx : -1); 415 /* Parse and send message */
416 assert(m);
832 417
418 uint8_t parsed [MSI_MAXMSG_SIZE];
833 419
834 if (!msg->csettings.exists) {/**/ 420 uint8_t *it = parsed;
835 LOGGER_WARNING("Peer sent invalid codec settings!"); 421 uint16_t size = 0;
836 send_error ( session, call, error_no_callid, msg->friend_id );
837 return 0;
838 }
839 422
840 if ( call ) { 423 if (msg->request.exists) {
841 if ( call->peers[0] == (uint32_t)msg->friend_id ) { 424 uint8_t cast = msg->request.value;
842 if (call->state == msi_CallInviting) { 425 it = msg_parse_header_out(IDRequest, it, &cast,
843 /* The glare case. A calls B when at the same time 426 sizeof(cast), &size);
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 { 427 } else {
885 call = init_call ( session, 1, 0 ); 428 LOGGER_DEBUG("Must have request field");
886 429 return -1;
887 if ( !call ) {
888 LOGGER_ERROR("Starting call");
889 return 0;
890 }
891 } 430 }
892 431
893 if ( !msg->callid.exists ) { 432 if (msg->error.exists) {
894 send_error ( session, call, error_no_callid, msg->friend_id ); 433 uint8_t cast = msg->error.value;
895 terminate_call(session, call); 434 it = msg_parse_header_out(IDError, it, &cast,
896 return 0; 435 sizeof(cast), &size);
897 } 436 }
898 437
899 memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) ); 438 if (msg->capabilities.exists) {
900 call->state = msi_CallStarting; 439 it = msg_parse_header_out(IDCapabilities, it, &msg->capabilities.value,
901 440 sizeof(msg->capabilities.value), &size);
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
910static int handle_recv_start ( MSISession *session, MSICall *call, MSIMessage *msg )
911{
912 if ( !call ) {
913 LOGGER_WARNING("Session: %p Handling 'start' on no call");
914 return 0;
915 } 441 }
916 442
917 (void)msg; 443 if (it == parsed) {
918 444 LOGGER_WARNING("Parsing message failed; empty message");
919 LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id ); 445 return -1;
920
921 call->state = msi_CallActive;
922 invoke_callback(session, call->call_idx, msi_OnStart);
923 return 1;
924}
925
926static int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage *msg )
927{
928 if ( !call ) {
929 LOGGER_WARNING("Session: %p Handling 'start' on no call");
930 return 0;
931 } 446 }
932 447
933 LOGGER_DEBUG("Session: %p Handling 'reject' on call: %u", session, call->call_idx); 448 *it = 0;
934 449 size ++;
935 invoke_callback(session, call->call_idx, msi_OnReject);
936
937 send_reponse(session, call, ending, msg->friend_id);
938 terminate_call(session, call);
939
940 return 1;
941}
942 450
943static int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage *msg ) 451 if (m_msi_packet(m, friend_number, parsed, size)) {
944{ 452 LOGGER_DEBUG("Sent message");
945 if ( !call ) {
946 LOGGER_WARNING("Session: %p Handling 'start' on no call");
947 return 0; 453 return 0;
948 } 454 }
949 455
950 (void)msg; 456 return -1;
951
952 LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx);
953
954 invoke_callback(session, call->call_idx, msi_OnCancel);
955 terminate_call ( session, call );
956
957 return 1;
958} 457}
959 458int send_error (Messenger *m, uint32_t friend_number, MSIError error)
960static int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg )
961{ 459{
962 if ( !call ) { 460 /* Send error message */
963 LOGGER_WARNING("Session: %p Handling 'start' on no call"); 461 assert(m);
964 return 0;
965 }
966 462
967 LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx); 463 LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_number);
968 464
969 invoke_callback(session, call->call_idx, msi_OnEnd); 465 MSIMessage msg;
970 send_reponse(session, call, ending, msg->friend_id); 466 msg_init(&msg, requ_pop);
971 terminate_call ( session, call );
972
973 return 1;
974}
975
976/********** Response handlers **********/
977static int handle_recv_ringing ( MSISession *session, MSICall *call, MSIMessage *msg )
978{
979 if ( !call ) {
980 LOGGER_WARNING("Session: %p Handling 'start' on no call");
981 return 0;
982 }
983 467
984 (void)msg; 468 msg.error.exists = true;
469 msg.error.value = error;
985 470
986 if ( call->ringing_timer_id ) { 471 send_message (m, friend_number, &msg);
987 LOGGER_WARNING("Call already ringing"); 472 return 0;
988 return 0;
989 }
990
991 LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %d", session, call->call_idx );
992
993 call->ringing_timer_id = timer_alloc
994 ( session, handle_timeout, call->call_idx, call->ringing_tout_ms );
995 invoke_callback(session, call->call_idx, msi_OnRinging);
996 return 1;
997} 473}
998static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg ) 474int invoke_callback(MSICall *call, MSICallbackID cb)
999{ 475{
1000 if ( !call ) { 476 assert(call);
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
1007 LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx );
1008
1009 invoke_callback(session, call->call_idx, msi_OnSelfCSChange);
1010 477
1011 } else if ( call->state == msi_CallInviting ) { 478 if (call->session->callbacks[cb]) {
1012 LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx ); 479 LOGGER_DEBUG("Invoking callback function: %d", cb);
1013 480
1014 call->state = msi_CallActive; 481 if (call->session->callbacks[cb] (call->session->av, call) != 0) {
1015 482 LOGGER_WARNING("Callback state handling failed, sending error");
1016 MSIMessage *msg_start = msi_new_message ( TypeRequest, start ); 483 goto FAILURE;
1017 send_message ( session, call, msg_start, msg->friend_id ); 484 }
1018 free ( msg_start );
1019
1020
1021 flush_peer_csettings ( call, msg, 0 );
1022
1023 /* This is here in case of glare */
1024 timer_release(session->timer_handler, call->ringing_timer_id);
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 485
1032 return 1;
1033}
1034static int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage *msg )
1035{
1036 if ( !call ) {
1037 LOGGER_WARNING("Session: %p Handling 'start' on no call");
1038 return 0; 486 return 0;
1039 } 487 }
1040 488
1041 (void)msg; 489FAILURE:
1042 490 /* If no callback present or error happened while handling,
1043 LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx ); 491 * an error message will be sent to friend
492 */
1044 493
1045 invoke_callback(session, call->call_idx, msi_OnEnd); 494 if (call->error == msi_ENone)
1046 terminate_call ( session, call ); 495 call->error = msi_EHandle;
1047 496
1048 return 1; 497 return -1;
1049} 498}
1050static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg ) 499static MSICall *get_call (MSISession *session, uint32_t friend_number)
1051{ 500{
1052 if ( !call ) { 501 assert(session);
1053 LOGGER_WARNING("Handling 'error' on non-existing call!");
1054 return -1;
1055 }
1056
1057 LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx );
1058
1059 invoke_callback(session, call->call_idx, msi_OnEnd);
1060
1061 /* Handle error accordingly */
1062 if ( msg->reason.exists ) {
1063 /* TODO */
1064 }
1065 502
1066 terminate_call ( session, call ); 503 if (session->calls == NULL || session->calls_tail < friend_number)
504 return NULL;
1067 505
1068 return 1; 506 return session->calls[friend_number];
1069} 507}
1070 508MSICall *new_call (MSISession *session, uint32_t friend_number)
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{ 509{
1106 LOGGER_DEBUG("Got msi message"); 510 assert(session);
1107 /* Unused */
1108 (void)messenger;
1109 511
1110 MSISession *session = object; 512 MSICall *rc = calloc(sizeof(MSICall), 1);
1111 MSIMessage *msg;
1112
1113 if ( !length ) {
1114 LOGGER_WARNING("Length param negative");
1115 return;
1116 }
1117
1118 msg = parse_recv ( data, length );
1119
1120 if ( !msg ) {
1121 LOGGER_WARNING("Error parsing message");
1122 return;
1123 } else {
1124 LOGGER_DEBUG("Successfully parsed message");
1125 }
1126
1127 msg->friend_id = source;
1128 513
1129 pthread_mutex_lock(session->mutex); 514 if (rc == NULL)
1130 515 return NULL;
1131 /* Find what call */
1132 MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL;
1133 516
1134 /* Now handle message */ 517 rc->session = session;
518 rc->friend_number = friend_number;
1135 519
1136 if ( msg->request.exists ) { /* Handle request */ 520 if (session->calls == NULL) { /* Creating */
521 session->calls = calloc (sizeof(MSICall *), friend_number + 1);
1137 522
1138 switch (msg->request.value) { 523 if (session->calls == NULL) {
1139 case invite: 524 free(rc);
1140 handle_recv_invite ( session, call, msg ); 525 return NULL;
1141 break; 526 }
1142 527
1143 case start: 528 session->calls_tail = session->calls_head = friend_number;
1144 handle_recv_start ( session, call, msg );
1145 break;
1146 529
1147 case cancel: 530 } else if (session->calls_tail < friend_number) { /* Appending */
1148 handle_recv_cancel ( session, call, msg ); 531 void *tmp = realloc(session->calls, sizeof(MSICall *) * (friend_number + 1));
1149 break;
1150 532
1151 case reject: 533 if (tmp == NULL) {
1152 handle_recv_reject ( session, call, msg ); 534 free(rc);
1153 break; 535 return NULL;
1154
1155 case end:
1156 handle_recv_end ( session, call, msg );
1157 break;
1158 } 536 }
1159 537
1160 } else if ( msg->response.exists ) { /* Handle response */ 538 session->calls = tmp;
1161 539
1162 /* Got response so cancel timer */ 540 /* Set fields in between to null */
1163 if ( call ) timer_release(session->timer_handler, call->request_timer_id); 541 uint32_t i = session->calls_tail + 1;
1164 542
1165 switch (msg->response.value) { 543 for (; i < friend_number; i ++)
1166 case ringing: 544 session->calls[i] = NULL;
1167 handle_recv_ringing ( session, call, msg );
1168 break;
1169
1170 case starting:
1171 handle_recv_starting ( session, call, msg );
1172 break;
1173 545
1174 case ending: 546 rc->prev = session->calls[session->calls_tail];
1175 handle_recv_ending ( session, call, msg ); 547 session->calls[session->calls_tail]->next = rc;
1176 break;
1177 548
1178 case error: 549 session->calls_tail = friend_number;
1179 handle_recv_error ( session, call, msg );
1180 break;
1181 }
1182 550
1183 } else { 551 } else if (session->calls_head > friend_number) { /* Inserting at front */
1184 LOGGER_WARNING("Invalid message: no resp nor requ headers"); 552 rc->next = session->calls[session->calls_head];
553 session->calls[session->calls_head]->prev = rc;
554 session->calls_head = friend_number;
1185 } 555 }
1186 556
1187 free ( msg ); 557 session->calls[friend_number] = rc;
1188 558 return rc;
1189 pthread_mutex_unlock(session->mutex);
1190} 559}
1191 560void kill_call (MSICall *call)
1192
1193
1194/********** User functions **********/
1195void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata )
1196{ 561{
1197 session->callbacks[id].first = callback; 562 /* Assume that session mutex is locked */
1198 session->callbacks[id].second = userdata; 563 if (call == NULL)
1199} 564 return;
1200
1201
1202MSISession *msi_new ( Messenger *messenger, int32_t max_calls )
1203{
1204 if (messenger == NULL) {
1205 LOGGER_ERROR("Could not init session on empty messenger!");
1206 return NULL;
1207 }
1208
1209 if ( !max_calls ) {
1210 LOGGER_WARNING("Invalid max call treshold!");
1211 return NULL;
1212 }
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 565
1241 if (create_recursive_mutex(retu->mutex) != 0) { 566 LOGGER_DEBUG("Killing call: %p", call);
1242 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
1243 goto error;
1244 }
1245 567
1246 retu->messenger_handle = messenger; 568 MSISession *session = call->session;
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 569
1252 m_callback_msi_packet(messenger, msi_handle_packet, retu ); 570 MSICall *prev = call->prev;
571 MSICall *next = call->next;
1253 572
1254 /* This is called when remote terminates session */ 573 if (prev)
1255 m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, retu); 574 prev->next = next;
575 else if (next)
576 session->calls_head = next->friend_number;
577 else goto CLEAR_CONTAINER;
1256 578
1257 LOGGER_DEBUG("New msi session: %p max calls: %u", retu, max_calls); 579 if (next)
1258 return retu; 580 next->prev = prev;
581 else if (prev)
582 session->calls_tail = prev->friend_number;
583 else goto CLEAR_CONTAINER;
1259 584
1260error: 585 session->calls[call->friend_number] = NULL;
586 free(call);
587 return;
1261 588
1262 if (retu->timer_handler) { 589CLEAR_CONTAINER:
1263 free(((TimerHandler *)retu->timer_handler)->timers); 590 session->calls_head = session->calls_tail = 0;
1264 free(retu->timer_handler); 591 free(session->calls);
1265 } 592 free(call);
1266 593 session->calls = NULL;
1267 free(retu->calls);
1268 free(retu);
1269 return NULL;
1270} 594}
1271 595void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data)
1272
1273int msi_kill ( MSISession *session )
1274{ 596{
1275 if (session == NULL) { 597 (void)m;
1276 LOGGER_ERROR("Tried to terminate non-existing session"); 598 MSISession *session = data;
1277 return -1;
1278 }
1279 599
1280 m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL); 600 switch (status) {
1281 pthread_mutex_lock(session->mutex); 601 case 0: { /* Friend is now offline */
602 LOGGER_DEBUG("Friend %d is now offline", friend_number);
1282 603
1283 /* Cancel active calls */ 604 pthread_mutex_lock(session->mutex);
1284 int32_t idx = 0; 605 MSICall *call = get_call(session, friend_number);
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 }
1300
1301 free(((TimerHandler *)session->timer_handler)->timers);
1302 free(session->timer_handler);
1303
1304 free ( session->calls );
1305 pthread_mutex_unlock(session->mutex);
1306 pthread_mutex_destroy(session->mutex);
1307
1308 LOGGER_DEBUG("Terminated session: %p", session);
1309 free ( session );
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 606
607 if (call == NULL) {
608 pthread_mutex_unlock(session->mutex);
609 return;
610 }
1323 611
1324 int i = 0; 612 invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */
1325 613 kill_call(call);
1326 for (; i < session->max_calls; i ++)
1327 if (session->calls[i] && session->calls[i]->peers[0] == friend_id) {
1328 LOGGER_ERROR("Already in a call with friend %d", friend_id);
1329 pthread_mutex_unlock(session->mutex); 614 pthread_mutex_unlock(session->mutex);
1330 return msi_ErrorAlreadyInCallWithPeer;
1331 } 615 }
616 break;
1332 617
1333 618 default:
1334 MSICall *call = init_call ( session, 1, rngsec ); /* Just one peer for now */ 619 break;
1335
1336 if ( !call ) {
1337 pthread_mutex_unlock(session->mutex);
1338 LOGGER_ERROR("Cannot handle more calls");
1339 return msi_ErrorReachedCallLimit;
1340 } 620 }
1341
1342 *call_index = call->call_idx;
1343
1344 t_randomstr ( call->id, sizeof(call->id) );
1345
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} 621}
1366 622void handle_init (MSICall* call, const MSIMessage* msg)
1367int msi_hangup ( MSISession *session, int32_t call_index )
1368{ 623{
1369 pthread_mutex_lock(session->mutex); 624 assert(call);
1370 LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index); 625 LOGGER_DEBUG("Session: %p Handling 'init' friend: %d", call->session, call->friend_number);
1371 626
1372 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 627 if (!msg->capabilities.exists) {
1373 LOGGER_ERROR("Invalid call index!"); 628 LOGGER_WARNING("Session: %p Invalid capabilities on 'init'");
1374 pthread_mutex_unlock(session->mutex); 629 call->error = msi_EInvalidMessage;
1375 return msi_ErrorNoCall; 630 goto FAILURE;
1376 } 631 }
632
633 switch (call->state)
634 {
635 case msi_CallInactive: {
636 /* Call requested */
637 call->peer_capabilities = msg->capabilities.value;
638 call->state = msi_CallRequested;
1377 639
1378 if ( session->calls[call_index]->state != msi_CallActive ) { 640 if (invoke_callback(call, msi_OnInvite) == -1)
1379 LOGGER_ERROR("Call is not active!"); 641 goto FAILURE;
1380 pthread_mutex_unlock(session->mutex); 642 }
1381 return msi_ErrorInvalidState; 643 break;
644
645 case msi_CallActive: {
646 /* If peer sent init while the call is already
647 * active it's probable that he is trying to
648 * re-call us while the call is not terminated
649 * on our side. We can assume that in this case
650 * we can automatically answer the re-call.
651 */
652
653 LOGGER_INFO("Friend is recalling us");
654
655 MSIMessage msg;
656 msg_init(&msg, requ_push);
657
658 msg.capabilities.exists = true;
659 msg.capabilities.value = call->self_capabilities;
660
661 send_message (call->session->messenger, call->friend_number, &msg);
662
663 /* If peer changed capabilities during re-call they will
664 * be handled accordingly during the next step
665 */
666 }
667 break;
668
669 default: {
670 LOGGER_WARNING("Session: %p Invalid state on 'init'");
671 call->error = msi_EInvalidState;
672 goto FAILURE;
673 }
674 break;
1382 } 675 }
1383 676
1384 MSIMessage *msg_end = msi_new_message ( TypeRequest, end ); 677 return;
1385 678FAILURE:
1386 /* hangup for each peer */ 679 send_error(call->session->messenger, call->friend_number, call->error);
1387 int it = 0; 680 kill_call(call);
1388
1389 for ( ; it < session->calls[call_index]->peer_count; it ++ )
1390 send_message ( session, session->calls[call_index], msg_end, session->calls[call_index]->peers[it] );
1391
1392 session->calls[call_index]->state = msi_CallOver;
1393
1394 free ( msg_end );
1395
1396 session->calls[call_index]->request_timer_id =
1397 timer_alloc ( session, handle_timeout, call_index, m_deftout );
1398
1399 pthread_mutex_unlock(session->mutex);
1400 return 0;
1401} 681}
1402 682void handle_push (MSICall *call, const MSIMessage *msg)
1403int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings )
1404{ 683{
1405 pthread_mutex_lock(session->mutex); 684 assert(call);
1406 LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index);
1407 685
1408 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 686 LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_number);
1409 LOGGER_ERROR("Invalid call index!");
1410 pthread_mutex_unlock(session->mutex);
1411 return msi_ErrorNoCall;
1412 }
1413 687
1414 if ( session->calls[call_index]->state != msi_CallStarting ) { 688 if (!msg->capabilities.exists) {
1415 LOGGER_ERROR("Call is in invalid state!"); 689 LOGGER_WARNING("Session: %p Invalid capabilities on 'push'");
1416 pthread_mutex_unlock(session->mutex); 690 call->error = msi_EInvalidMessage;
1417 return msi_ErrorInvalidState; 691 goto FAILURE;
1418 } 692 }
1419 693
1420 MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting ); 694 switch (call->state) {
1421 695 case msi_CallActive: {
1422 session->calls[call_index]->csettings_local = *csettings; 696 /* Only act if capabilities changed */
1423 697 if (call->peer_capabilities != msg->capabilities.value) {
1424 msi_msg_set_csettings(msg_starting, csettings); 698 LOGGER_INFO("Friend is changing capabilities to: %u", msg->capabilities.value);
1425 699
1426 send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] ); 700 call->peer_capabilities = msg->capabilities.value;
1427 free ( msg_starting );
1428
1429 session->calls[call_index]->state = msi_CallActive;
1430
1431 pthread_mutex_unlock(session->mutex);
1432 return 0;
1433}
1434 701
1435int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ) 702 if (invoke_callback(call, msi_OnCapabilities) == -1)
1436{ 703 goto FAILURE;
1437 pthread_mutex_lock(session->mutex); 704 }
1438 LOGGER_DEBUG("Session: %p Canceling call: %u; reason: %s", session, call_index, reason ? reason : "Unknown"); 705 }
706 break;
1439 707
1440 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 708 case msi_CallRequesting: {
1441 LOGGER_ERROR("Invalid call index!"); 709 LOGGER_INFO("Friend answered our call");
1442 pthread_mutex_unlock(session->mutex);
1443 return msi_ErrorNoCall;
1444 }
1445 710
1446 if ( session->calls[call_index]->state != msi_CallInviting ) { 711 /* Call started */
1447 LOGGER_ERROR("Call is in invalid state: %u", session->calls[call_index]->state); 712 call->peer_capabilities = msg->capabilities.value;
1448 pthread_mutex_unlock(session->mutex); 713 call->state = msi_CallActive;
1449 return msi_ErrorInvalidState;
1450 }
1451 714
1452 MSIMessage *msg_cancel = msi_new_message ( TypeRequest, cancel ); 715 if (invoke_callback(call, msi_OnStart) == -1)
716 goto FAILURE;
1453 717
1454 /* FIXME */ 718 }
1455#if 0 719 break;
1456 720
1457 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) { 721 /* Pushes during initialization state are ignored */
1458 MSIReasonStrType reason_cast; 722 case msi_CallInactive:
1459 memset(reason_cast, '\0', sizeof(MSIReasonStrType)); 723 case msi_CallRequested: {
1460 memcpy(reason_cast, reason, strlen(reason)); 724 LOGGER_WARNING("Ignoring invalid push");
1461 msi_msg_set_reason(msg_cancel, reason_cast); 725 }
726 break;
1462 } 727 }
1463 728
1464#else 729 return;
1465 (void)reason;
1466
1467#endif
1468 730
1469 send_message ( session, session->calls[call_index], msg_cancel, peer ); 731FAILURE:
1470 free ( msg_cancel ); 732 send_error(call->session->messenger, call->friend_number, call->error);
1471 733 kill_call(call);
1472 terminate_call ( session, session->calls[call_index] );
1473 pthread_mutex_unlock(session->mutex);
1474
1475 return 0;
1476} 734}
1477 735void handle_pop (MSICall *call, const MSIMessage *msg)
1478int msi_reject ( MSISession *session, int32_t call_index, const char *reason )
1479{ 736{
1480 pthread_mutex_lock(session->mutex); 737 assert(call);
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 738
1497 /* FIXME */ 739 LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_number);
1498#if 0
1499 740
1500 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) { 741 /* callback errors are ignored */
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 742
1512 send_message ( session, session->calls[call_index], msg_reject, 743 if (msg->error.exists) {
1513 session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] ); 744 LOGGER_WARNING("Friend detected an error: %d", msg->error.value);
1514 free ( msg_reject ); 745 call->error = msg->error.value;
746 invoke_callback(call, msi_OnError);
1515 747
1516 session->calls[call_index]->state = msi_CallOver; 748 } else switch (call->state) {
1517 session->calls[call_index]->request_timer_id = 749 case msi_CallInactive: {
1518 timer_alloc ( session, handle_timeout, call_index, m_deftout ); 750 LOGGER_ERROR("Handling what should be impossible case");
751 abort();
752 }
753 break;
1519 754
1520 pthread_mutex_unlock(session->mutex); 755 case msi_CallActive: {
1521 return 0; 756 /* Hangup */
1522} 757 LOGGER_INFO("Friend hung up on us");
758 invoke_callback(call, msi_OnEnd);
759 }
760 break;
1523 761
1524int msi_stopcall ( MSISession *session, int32_t call_index ) 762 case msi_CallRequesting: {
1525{ 763 /* Reject */
1526 pthread_mutex_lock(session->mutex); 764 LOGGER_INFO("Friend rejected our call");
1527 LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index); 765 invoke_callback(call, msi_OnEnd);
766 }
767 break;
1528 768
1529 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 769 case msi_CallRequested: {
1530 pthread_mutex_unlock(session->mutex); 770 /* Cancel */
1531 return msi_ErrorNoCall; 771 LOGGER_INFO("Friend canceled call invite");
772 invoke_callback(call, msi_OnEnd);
773 }
774 break;
1532 } 775 }
1533 776
1534 /* just terminate it */ 777 kill_call (call);
1535
1536 terminate_call ( session, session->calls[call_index] );
1537
1538 pthread_mutex_unlock(session->mutex);
1539 return 0;
1540} 778}
1541 779void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object)
1542int msi_change_csettings(MSISession *session, int32_t call_index, const MSICSettings *csettings)
1543{ 780{
1544 pthread_mutex_lock(session->mutex); 781 LOGGER_DEBUG("Got msi message");
1545
1546 LOGGER_DEBUG("Changing media on call: %d", call_index);
1547
1548 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1549 LOGGER_ERROR("Invalid call index!");
1550 pthread_mutex_unlock(session->mutex);
1551 return msi_ErrorNoCall;
1552 }
1553
1554 MSICall *call = session->calls[call_index];
1555 782
1556 if ( call->state != msi_CallActive ) { 783 MSISession *session = object;
1557 LOGGER_ERROR("Call is not active!"); 784 MSIMessage msg;
1558 pthread_mutex_unlock(session->mutex);
1559 return msi_ErrorInvalidState;
1560 }
1561 785
1562 MSICSettings *local = &call->csettings_local; 786 if (msg_parse_in (&msg, data, length) == -1) {
1563 787 LOGGER_WARNING("Error parsing message");
1564 if ( 788 send_error(m, friend_number, msi_EInvalidMessage);
1565 local->call_type == csettings->call_type && 789 return;
1566 local->video_bitrate == csettings->video_bitrate && 790 } else {
1567 local->max_video_width == csettings->max_video_width && 791 LOGGER_DEBUG("Successfully parsed message");
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 } 792 }
1577 793
1578 *local = *csettings;
1579
1580 MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite );
1581
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); 794 pthread_mutex_lock(session->mutex);
795 MSICall *call = get_call(session, friend_number);
1596 796
1597 TimerHandler *timer = session->timer_handler; 797 if (call == NULL) {
1598 798 if (msg.request.value != requ_init) {
1599 uint64_t time = current_time_monotonic(); 799 send_error(m, friend_number, msi_EStrayMessage);
800 pthread_mutex_unlock(session->mutex);
801 return;
802 }
1600 803
1601 while ( timer->timers[0] && timer->timers[0]->timeout < time ) { 804 call = new_call(session, friend_number);
1602 LOGGER_DEBUG("Executing timer assigned at: %d", timer->timers[0]->timeout);
1603 805
1604 int id = timer->timers[0]->id; 806 if (call == NULL) {
1605 timer->timers[0]->func(timer->timers[0]); 807 send_error(m, friend_number, msi_ESystem);
808 pthread_mutex_unlock(session->mutex);
809 return;
810 }
811 }
1606 812
1607 /* In case function has released timer */ 813 switch (msg.request.value) {
1608 if (timer->timers[0] && timer->timers[0]->id == id) 814 case requ_init:
1609 timer_release(timer, id); 815 handle_init(call, &msg);
816 break;
817 case requ_push:
818 handle_push(call, &msg);
819 break;
820 case requ_pop:
821 handle_pop(call, &msg); /* always kills the call */
822 break;
1610 } 823 }
1611 824
1612 pthread_mutex_unlock(session->mutex); 825 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..763166cd 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,369 @@
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 34
62 35
63/** 36int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
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 37
75 if ( !retu ) {
76 LOGGER_WARNING("Alloc failed! Program might misbehave!");
77 return NULL;
78 }
79 38
80 memcpy(&retu->sequnum, payload, sizeof(retu->sequnum)); 39RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friendnumber,
81 retu->sequnum = ntohs(retu->sequnum); 40 BWControler *bwc, void *cs,
82 41 int (*mcb) (void *, struct RTPMessage *))
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
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
127 uint8_t x;
128
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
135 return retu;
136}
137
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
210 timestamp = htonl(header->timestamp);
211 memcpy(it, &timestamp, sizeof(timestamp));
212 it += 4;
213 ssrc = htonl(header->ssrc);
214 memcpy(it, &ssrc, sizeof(ssrc));
215
216 uint8_t x;
217 78
218 for ( x = 0; x < cc; x++ ) { 79 LOGGER_DEBUG("Terminated RTP session: %p", session);
219 it += 4;
220 csrc = htonl(header->csrc[x]);
221 memcpy(it, &csrc, sizeof(csrc));
222 }
223
224 return it + 4;
225}
226
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{
232 uint8_t *it = payload;
233 uint16_t length;
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
255 return it + 4;
256}
257
258/**
259 * Builds header from control session values.
260 */
261RTPHeader *build_header ( RTPSession *session )
262{
263 RTPHeader *retu = calloc ( 1, sizeof (RTPHeader) );
264
265 if ( !retu ) {
266 LOGGER_WARNING("Alloc failed! Program might misbehave!");
267 return NULL;
268 }
269
270 ADD_FLAG_VERSION ( retu, session->version );
271 ADD_FLAG_PADDING ( retu, session->padding );
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
277 retu->sequnum = session->sequnum;
278 retu->timestamp = current_time_monotonic(); /* milliseconds */
279 retu->ssrc = session->ssrc;
280
281 int i;
282
283 for ( i = 0; i < session->cc; i++ )
284 retu->csrc[i] = session->csrc[i];
285
286 retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
287 80
288 return retu; 81 rtp_stop_receiving (session);
82 free (session);
289} 83}
290 84int rtp_allow_receiving(RTPSession *session)
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{ 85{
298 RTPMessage *retu = calloc(1, sizeof (RTPMessage)); 86 if (session == NULL)
299 87 return -1;
300 retu->header = extract_header ( data, length ); /* It allocates memory and all */
301
302 if ( !retu->header ) {
303 LOGGER_WARNING("Header failed to extract!");
304 free(retu);
305 return NULL;
306 }
307
308 uint16_t from_pos = retu->header->length;
309 retu->length = length - from_pos;
310
311
312
313 if ( GET_FLAG_EXTENSION ( retu->header ) ) {
314 retu->ext_header = extract_ext_header ( data + from_pos, length );
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 {
325 retu->ext_header = NULL;
326 }
327 88
328 if ( length - from_pos <= MAX_RTP_SIZE ) 89 if (m_callback_rtp_packet(session->m, session->friend_number, session->payload_type,
329 memcpy ( retu->data, data + from_pos, length - from_pos ); 90 handle_rtp_packet, session) == -1) {
330 else { 91 LOGGER_WARNING("Failed to register rtp receive handler");
331 LOGGER_WARNING("Invalid length!"); 92 return -1;
332 rtp_free_msg(NULL, retu);
333 return NULL;
334 } 93 }
335 94
336 retu->next = NULL; 95 LOGGER_DEBUG("Started receiving on session: %p", session);
337 96 return 0;
338 return retu;
339} 97}
340 98int rtp_stop_receiving(RTPSession *session)
341/**
342 * Callback for networking core.
343 */
344int rtp_handle_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object )
345{ 99{
346 RTPSession *session = object; 100 if (session == NULL)
347 RTPMessage *msg;
348
349 if ( !session || length < 13 ) { /* 12 is the minimum length for rtp + desc. byte */
350 LOGGER_WARNING("No session or invalid length of received buffer!");
351 return -1; 101 return -1;
352 }
353
354 msg = msg_parse ( data + 1, length - 1 );
355
356 if ( !msg ) {
357 LOGGER_WARNING("Could not parse message!");
358 return -1;
359 }
360 102
361 /* Check if message came in late */ 103 m_callback_rtp_packet(session->m, session->friend_number, session->payload_type, NULL, NULL);
362 if ( check_late_message(session, msg) < 0 ) { /* Not late */
363 session->rsequnum = msg->header->sequnum;
364 session->timestamp = msg->header->timestamp;
365 }
366
367 queue_message(session, msg);
368 104
105 LOGGER_DEBUG("Stopped receiving on session: %p", session);
369 return 0; 106 return 0;
370} 107}
371 108int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length)
372/**
373 * Allocate message and store data there
374 */
375RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length )
376{ 109{
377 if ( !session ) { 110 if (!session) {
378 LOGGER_WARNING("No session!"); 111 LOGGER_WARNING("No session!");
379 return NULL; 112 return -1;
380 } 113 }
381 114
382 uint8_t *from_pos; 115 uint8_t rdata[length + sizeof(struct RTPHeader) + 1];
383 RTPMessage *retu = calloc(1, sizeof (RTPMessage)); 116 memset(rdata, 0, sizeof(rdata));
384 117
385 if ( !retu ) { 118 rdata[0] = session->payload_type;
386 LOGGER_WARNING("Alloc failed! Program might misbehave!");
387 return NULL;
388 }
389 119
390 /* Sets header values and copies the extension header in retu */ 120 struct RTPHeader *header = (struct RTPHeader *)(rdata + 1);
391 retu->header = build_header ( session ); /* It allocates memory and all */
392 retu->ext_header = session->ext_header;
393 121
122 header->ve = 2;
123 header->pe = 0;
124 header->xe = 0;
125 header->cc = 0;
394 126
395 uint32_t total_length = length + retu->header->length + 1; 127 header->ma = 0;
128 header->pt = session->payload_type % 128;
396 129
397 retu->data[0] = session->prefix; 130 header->sequnum = htons(session->sequnum);
131 header->timestamp = htonl(current_time_monotonic());
132 header->ssrc = htonl(session->ssrc);
398 133
399 if ( retu->ext_header ) { 134 header->cpart = 0;
400 total_length += ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 ); 135 header->tlen = htons(length);
401 136
402 from_pos = add_header ( retu->header, retu->data + 1 ); 137 if (MAX_CRYPTO_DATA_SIZE > length + sizeof(struct RTPHeader) + 1) {
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 }
407 138
408 /* 139 /**
409 * Parses the extension header into the message 140 * The lenght is lesser than the maximum allowed lenght (including header)
410 * Of course if any 141 * Send the packet in single piece.
411 */ 142 */
412 143
413 /* Appends data on to retu->data */ 144 memcpy(rdata + 1 + sizeof(struct RTPHeader), data, length);
414 memcpy ( from_pos, data, length );
415 145
416 retu->length = total_length; 146 if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata, sizeof(rdata)))
147 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s", sizeof(rdata), strerror(errno));
148 } else {
417 149
418 retu->next = NULL; 150 /**
151 * The lenght is greater than the maximum allowed lenght (including header)
152 * Send the packet in multiple pieces.
153 */
419 154
420 return retu; 155 uint16_t sent = 0;
421} 156 uint16_t piece = MAX_CRYPTO_DATA_SIZE - (sizeof(struct RTPHeader) + 1);
422 157
158 while ((length - sent) + sizeof(struct RTPHeader) + 1 > MAX_CRYPTO_DATA_SIZE) {
159 memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
423 160
161 if (-1 == send_custom_lossy_packet(session->m, session->friend_number,
162 rdata, piece + sizeof(struct RTPHeader) + 1))
163 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
164 piece + sizeof(struct RTPHeader) + 1, strerror(errno));
424 165
425int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ) 166 sent += piece;
426{ 167 header->cpart = htons(sent);
427 RTPMessage *msg = rtp_new_message (session, data, length); 168 }
428 169
429 if ( !msg ) return -1; 170 /* Send remaining */
171 piece = length - sent;
430 172
431 int ret = send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length); 173 if (piece) {
174 memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
432 175
433 if ( 0 != ret) { 176 if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata,
434 LOGGER_WARNING("Failed to send full packet (len: %d)! error: %i", length, ret); 177 piece + sizeof(struct RTPHeader) + 1))
435 rtp_free_msg ( session, msg ); 178 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
436 return rtp_ErrorSending; 179 piece + sizeof(struct RTPHeader) + 1, strerror(errno));
180 }
437 } 181 }
438 182
439 /* Set sequ number */ 183 session->sequnum ++;
440 session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1;
441 rtp_free_msg ( session, msg );
442
443 return 0; 184 return 0;
444} 185}
445 186
446void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) 187
188bool chloss (const RTPSession *session, const struct RTPHeader *header)
447{ 189{
448 if ( !session ) { 190 if (ntohl(header->timestamp) < session->rtimestamp) {
449 if ( msg->ext_header ) { 191 uint16_t hosq, lost = 0;
450 free ( msg->ext_header->table ); 192
451 free ( msg->ext_header ); 193 hosq = ntohs(header->sequnum);
452 } 194
453 } else { 195 lost = (hosq > session->rsequnum) ?
454 if ( msg->ext_header && session->ext_header != msg->ext_header ) { 196 (session->rsequnum + 65535) - hosq :
455 free ( msg->ext_header->table ); 197 session->rsequnum - hosq;
456 free ( msg->ext_header ); 198
457 } 199 puts ("Lost packet");
200 while (lost --)
201 bwc_add_lost(session->bwc ,0);
202
203 return true;
458 } 204 }
459 205
460 free ( msg->header ); 206 return false;
461 free ( msg );
462} 207}
463 208struct RTPMessage *new_message (size_t allocate_len, const uint8_t *data, uint16_t data_length)
464RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
465{ 209{
466 RTPSession *retu = calloc(1, sizeof(RTPSession)); 210 assert(allocate_len >= data_length);
467
468 if ( !retu ) {
469 LOGGER_WARNING("Alloc failed! Program might misbehave!");
470 return NULL;
471 }
472 211
473 if ( -1 == m_callback_rtp_packet(messenger, friend_num, payload_type, rtp_handle_packet, retu)) { 212 struct RTPMessage *msg = calloc(sizeof(struct RTPMessage) + (allocate_len - sizeof(struct RTPHeader)), 1);
474 LOGGER_ERROR("Error setting custom register handler for rtp session");
475 free(retu);
476 return NULL;
477 }
478 213
479 LOGGER_DEBUG("Registered packet handler: pt: %d; fid: %d", payload_type, friend_num); 214 msg->len = data_length - sizeof(struct RTPHeader);
215 memcpy(&msg->header, data, data_length);
480 216
481 retu->version = RTP_VERSION; /* It's always 2 */ 217 msg->header.sequnum = ntohs(msg->header.sequnum);
482 retu->padding = 0; /* If some additional data is needed about the packet */ 218 msg->header.timestamp = ntohl(msg->header.timestamp);
483 retu->extension = 0; /* If extension to header is needed */ 219 msg->header.ssrc = ntohl(msg->header.ssrc);
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 220
490 retu->dest = friend_num; 221 msg->header.cpart = ntohs(msg->header.cpart);
222 msg->header.tlen = ntohs(msg->header.tlen);
491 223
492 retu->rsequnum = retu->sequnum = 0; 224 return msg;
225}
226int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object)
227{
228 (void) m;
229 (void) friendnumber;
493 230
494 retu->ext_header = NULL; /* When needed allocate */ 231 RTPSession *session = object;
495 232
233 data ++;
234 length--;
496 235
497 if ( !(retu->csrc = calloc(1, sizeof (uint32_t))) ) { 236 if (!session || length < sizeof (struct RTPHeader)) {
498 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 237 LOGGER_WARNING("No session or invalid length of received buffer!");
499 free(retu); 238 return -1;
500 return NULL;
501 } 239 }
502 240
503 retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */ 241 const struct RTPHeader *header = (struct RTPHeader *) data;
504 242
505 /* Also set payload type as prefix */ 243 if (header->pt != session->payload_type % 128) {
506 retu->prefix = payload_type; 244 LOGGER_WARNING("Invalid payload type with the session");
245 return -1;
246 }
507 247
508 /* 248 if (ntohs(header->cpart) >= ntohs(header->tlen)) {
509 * 249 /* Never allow this case to happen */
510 */ 250 return -1;
511 return retu; 251 }
512}
513 252
514void rtp_kill ( RTPSession *session, Messenger *messenger ) 253 bwc_feed_avg(session->bwc, length);
515{ 254
516 if ( !session ) return; 255 if (ntohs(header->tlen) == length - sizeof (struct RTPHeader)) {
256 /* The message is sent in single part */
257
258 /* Only allow messages which have arrived in order;
259 * drop late messages
260 */
261 if (chloss(session, header)) {
262 return 0;
263 } else {
264 /* Message is not late; pick up the latest parameters */
265 session->rsequnum = ntohs(header->sequnum);
266 session->rtimestamp = ntohl(header->timestamp);
267 }
517 268
518 m_callback_rtp_packet(messenger, session->dest, session->prefix, NULL, NULL); 269 bwc_add_recv(session->bwc, length);
270
271 /* Invoke processing of active multiparted message */
272 if (session->mp) {
273 if (session->mcb)
274 session->mcb (session->cs, session->mp);
275 else
276 free(session->mp);
519 277
520 free ( session->ext_header ); 278 session->mp = NULL;
521 free ( session->csrc ); 279 }
522 280
523 LOGGER_DEBUG("Terminated RTP session: %p", session); 281 /* The message came in the allowed time;
282 * process it only if handler for the session is present.
283 */
524 284
525 /* And finally free session */ 285 if (!session->mcb)
526 free ( session ); 286 return 0;
527 287
288 return session->mcb (session->cs, new_message(length, data, length));
289 } else {
290 /* The message is sent in multiple parts */
291
292 if (session->mp) {
293 /* There are 2 possible situations in this case:
294 * 1) being that we got the part of already processing message.
295 * 2) being that we got the part of a new/old message.
296 *
297 * We handle them differently as we only allow a single multiparted
298 * processing message
299 */
300
301 if (session->mp->header.sequnum == ntohs(header->sequnum) &&
302 session->mp->header.timestamp == ntohl(header->timestamp)) {
303 /* First case */
304
305 /* Make sure we have enough allocated memory */
306 if (session->mp->header.tlen - session->mp->len < length - sizeof(struct RTPHeader) ||
307 session->mp->header.tlen <= ntohs(header->cpart)) {
308 /* There happened to be some corruption on the stream;
309 * continue wihtout this part
310 */
311 return 0;
312 }
313
314 memcpy(session->mp->data + ntohs(header->cpart), data + sizeof(struct RTPHeader),
315 length - sizeof(struct RTPHeader));
316
317 session->mp->len += length - sizeof(struct RTPHeader);
318
319 bwc_add_recv(session->bwc, length);
320
321 if (session->mp->len == session->mp->header.tlen) {
322 /* Received a full message; now push it for the further
323 * processing.
324 */
325 if (session->mcb)
326 session->mcb (session->cs, session->mp);
327 else
328 free(session->mp);
329
330 session->mp = NULL;
331 }
332 } else {
333 /* Second case */
334
335 if (session->mp->header.timestamp > ntohl(header->timestamp))
336 /* The received message part is from the old message;
337 * discard it.
338 */
339 return 0;
340
341 /* Measure missing parts of the old message */
342 bwc_add_lost(session->bwc,
343 (session->mp->header.tlen - session->mp->len) +
344
345 /* Must account sizes of rtp headers too */
346 ((session->mp->header.tlen - session->mp->len) /
347 MAX_CRYPTO_DATA_SIZE) * sizeof(struct RTPHeader) );
348
349 /* Push the previous message for processing */
350 if (session->mcb)
351 session->mcb (session->cs, session->mp);
352 else
353 free(session->mp);
354
355 session->mp = NULL;
356 goto NEW_MULTIPARTED;
357 }
358 } else {
359 /* In this case threat the message as if it was received in order
360 */
361
362 /* This is also a point for new multiparted messages */
363NEW_MULTIPARTED:
364
365 /* Only allow messages which have arrived in order;
366 * drop late messages
367 */
368 if (chloss(session, header)) {
369 return 0;
370 } else {
371 /* Message is not late; pick up the latest parameters */
372 session->rsequnum = ntohs(header->sequnum);
373 session->rtimestamp = ntohl(header->timestamp);
374 }
375
376 bwc_add_recv(session->bwc, length);
377
378 /* Again, only store message if handler is present
379 */
380 if (session->mcb) {
381 session->mp = new_message(ntohs(header->tlen) + sizeof(struct RTPHeader), data, length);
382
383 /* Reposition data if necessary */
384 if (ntohs(header->cpart));
385
386 memmove(session->mp->data + ntohs(header->cpart), session->mp->data, session->mp->len);
387 }
388 }
389 }
390
391 return 0;
528} 392}
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..6a17f55d 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,1220 @@ 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 */
56
57 /** Required for monitoring changes in states */
58 uint8_t previous_self_capabilities;
59
60 pthread_mutex_t mutex[1];
61
62 struct ToxAVCall_s *prev;
63 struct ToxAVCall_s *next;
64} ToxAVCall;
60 65
61static const uint32_t jbuf_capacity = 6; 66struct ToxAV {
62static const uint8_t audio_index = 0, video_index = 1; 67 Messenger *m;
68 MSISession *msi;
63 69
64typedef struct _ToxAvCall { 70 /* Two-way storage: first is array of calls and second is list of calls with head and tail */
71 ToxAVCall **calls;
72 uint32_t calls_tail;
73 uint32_t calls_head;
65 pthread_mutex_t mutex[1]; 74 pthread_mutex_t mutex[1];
66 pthread_mutex_t mutex_encoding_audio[1]; 75
67 pthread_mutex_t mutex_encoding_video[1]; 76 PAIR(toxav_call_cb *, void *) ccb; /* Call callback */
68 pthread_mutex_t mutex_do[1]; 77 PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */
69 RTPSession *crtps[2]; /** Audio is first and video is second */ 78 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
70 CSSession *cs; 79 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
71 _Bool active; 80 PAIR(toxav_bit_rate_status_cb *, void *) bcb; /* Bit rate control callback */
72} ToxAvCall; 81
73 82 /** Decode time measures */
74struct _ToxAv { 83 int32_t dmssc; /** Measure count */
75 Messenger *messenger; 84 int32_t dmsst; /** Last cycle total */
76 MSISession *msi_session; /** Main msi session */ 85 int32_t dmssa; /** Average decoding time in ms */
77 ToxAvCall *calls; /** Per-call params */ 86
78 uint32_t max_calls; 87 uint32_t interval; /** Calculated interval */
79
80 PAIR(ToxAvAudioCallback, void *) acb;
81 PAIR(ToxAvVideoCallback, void *) vcb;
82
83 /* Decode time measure */
84 int32_t dectmsscount; /** Measure count */
85 int32_t dectmsstotal; /** Last cycle total */
86 int32_t avgdectms; /** Average decoding time in ms */
87}; 88};
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;
99
100} 114}
115uint32_t toxav_version_patch(void)
116{
117 return 0;
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;
105 131
106 if (av == NULL) { 132 if (tox == NULL) {
107 LOGGER_WARNING("Allocation failed!"); 133 rc = TOXAV_ERR_NEW_NULL;
108 return NULL; 134 goto END;
109 } 135 }
110 136
111 av->messenger = (Messenger *)messenger; 137 if (((Messenger *)tox)->msi_packet) {
112 av->msi_session = msi_new(av->messenger, max_calls); 138 rc = TOXAV_ERR_NEW_MULTIPLE;
113 av->msi_session->agent_handler = av; 139 goto END;
114 av->calls = calloc(sizeof(ToxAvCall), max_calls); 140 }
115 av->max_calls = max_calls;
116 141
117 unsigned int i; 142 av = calloc (sizeof(ToxAV), 1);
118 143
119 for (i = 0; i < max_calls; ++i) { 144 if (av == NULL) {
120 if (create_recursive_mutex(av->calls[i].mutex) != 0 ) { 145 LOGGER_WARNING("Allocation failed!");
121 LOGGER_WARNING("Failed to init call(%u) mutex!", i); 146 rc = TOXAV_ERR_NEW_MALLOC;
122 msi_kill(av->msi_session); 147 goto END;
148 }
123 149
124 free(av->calls); 150 if (create_recursive_mutex(av->mutex) != 0) {
125 free(av); 151 LOGGER_WARNING("Mutex creation failed!");
126 return NULL; 152 rc = TOXAV_ERR_NEW_MALLOC;
127 } 153 goto END;
128 } 154 }
129 155
130 return av; 156 av->m = (Messenger *)tox;
131} 157 av->msi = msi_new(av->m);
132 158
133void toxav_kill ( ToxAv *av ) 159 if (av->msi == NULL) {
134{ 160 pthread_mutex_destroy(av->mutex);
135 uint32_t i; 161 rc = TOXAV_ERR_NEW_MALLOC;
162 goto END;
163 }
136 164
137 for (i = 0; i < av->max_calls; i ++) { 165 av->interval = 200;
138 if ( av->calls[i].crtps[audio_index] ) 166 av->msi->av = av;
139 rtp_kill(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle);
140 167
168 msi_register_callback(av->msi, callback_invite, msi_OnInvite);
169 msi_register_callback(av->msi, callback_start, msi_OnStart);
170 msi_register_callback(av->msi, callback_end, msi_OnEnd);
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);
141 174
142 if ( av->calls[i].crtps[video_index] ) 175END:
143 rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle);
144 176
145 if ( av->calls[i].cs ) 177 if (error)
146 cs_kill(av->calls[i].cs); 178 *error = rc;
147 179
148 pthread_mutex_destroy(av->calls[i].mutex); 180 if (rc != TOXAV_ERR_NEW_OK) {
181 free(av);
182 av = NULL;
149 } 183 }
150 184
151 msi_kill(av->msi_session); 185 return av;
152
153 free(av->calls);
154 free(av);
155} 186}
156 187void toxav_kill(ToxAV *av)
157uint32_t toxav_do_interval(ToxAv *av)
158{ 188{
159 int i = 0; 189 if (av == NULL)
160 uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */ 190 return;
191
192 pthread_mutex_lock(av->mutex);
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 }
161 199
162 for (; i < av->max_calls; i ++) { 200 /* Msi kill will hang up all calls so just clean these calls */
163 pthread_mutex_lock(av->calls[i].mutex); 201 if (av->calls) {
202 ToxAVCall *it = call_get(av, av->calls_head);
164 203
165 if (av->calls[i].active) { 204 while (it) {
166 /* This should work. Video payload will always come in greater intervals */ 205 call_kill_transmission(it);
167 rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc); 206 it = call_remove(it); /* This will eventually free av->calls */
168 } 207 }
169
170 pthread_mutex_unlock(av->calls[i].mutex);
171 } 208 }
172 209
173 return rc < av->avgdectms ? 0 : rc - av->avgdectms; 210 pthread_mutex_unlock(av->mutex);
211 pthread_mutex_destroy(av->mutex);
212
213 free(av);
174} 214}
175 215Tox *toxav_get_tox(const ToxAV *av)
176void toxav_do(ToxAv *av) 216{
217 return (Tox *) av->m;
218}
219uint32_t toxav_iteration_interval(const ToxAV *av)
220{
221 /* If no call is active interval is 200 */
222 return av->calls ? av->interval : 200;
223}
224void toxav_iterate(ToxAV *av)
177{ 225{
178 msi_do(av->msi_session); 226 pthread_mutex_lock(av->mutex);
227
228 if (av->calls == NULL) {
229 pthread_mutex_unlock(av->mutex);
230 return;
231 }
179 232
180 uint64_t start = current_time_monotonic(); 233 uint64_t start = current_time_monotonic();
234 int32_t rc = 500;
181 235
182 uint32_t i = 0; 236 ToxAVCall *i = av->calls[av->calls_head];
183 237
184 for (; i < av->max_calls; i ++) { 238 for (; i; i = i->next) {
185 pthread_mutex_lock(av->calls[i].mutex); 239 if (i->active) {
240 pthread_mutex_lock(i->mutex);
241 pthread_mutex_unlock(av->mutex);
186 242
187 if (av->calls[i].active) { 243 ac_iterate(i->audio.second);
188 pthread_mutex_lock(av->calls[i].mutex_do); 244 vc_iterate(i->video.second);
189 pthread_mutex_unlock(av->calls[i].mutex); 245
190 cs_do(av->calls[i].cs); 246 if (i->msi_call->self_capabilities & msi_CapRAudio &&
191 pthread_mutex_unlock(av->calls[i].mutex_do); 247 i->msi_call->peer_capabilities & msi_CapSAudio)
192 } else { 248 rc = MIN(i->audio.second->lp_frame_duration, rc);
193 pthread_mutex_unlock(av->calls[i].mutex); 249
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);
253
254 uint32_t fid = i->friend_number;
255
256 pthread_mutex_unlock(i->mutex);
257 pthread_mutex_lock(av->mutex);
258
259 /* In case this call is popped from container stop iteration */
260 if (call_get(av, fid) != i)
261 break;
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} 280
281 pthread_mutex_lock(av->mutex);
282
283 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate))
284 || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))) {
285 rc = TOXAV_ERR_CALL_INVALID_BIT_RATE;
286 goto END;
287 }
213 288
214void toxav_register_audio_callback(ToxAv *av, ToxAvAudioCallback cb, void *userdata) 289 ToxAVCall *call = call_new(av, friend_number, error);
215{
216 av->acb.first = cb;
217 av->acb.second = userdata;
218}
219 290
220void toxav_register_video_callback(ToxAv *av, ToxAvVideoCallback cb, void *userdata) 291 if (call == NULL) {
221{ 292 rc = TOXAV_ERR_CALL_MALLOC;
222 av->vcb.first = cb; 293 goto END;
223 av->vcb.second = userdata; 294 }
224}
225 295
226int toxav_call (ToxAv *av, 296 call->audio_bit_rate = audio_bit_rate;
227 int32_t *call_index, 297 call->video_bit_rate = video_bit_rate;
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 298
235int toxav_hangup ( ToxAv *av, int32_t call_index ) 299 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
236{
237 return msi_hangup(av->msi_session, call_index);
238}
239 300
240int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) 301 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
241{ 302 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
242 return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings));
243}
244 303
245int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) 304 if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) {
246{ 305 call_remove(call);
247 return msi_reject(av->msi_session, call_index, reason); 306 rc = TOXAV_ERR_CALL_SYNC;
248} 307 goto END;
308 }
249 309
250int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) 310 call->msi_call->av_call = call;
251{
252 return msi_cancel(av->msi_session, call_index, peer_id, reason);
253}
254 311
255int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) 312END:
256{ 313 pthread_mutex_unlock(av->mutex);
257 return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); 314
258} 315 if (error)
316 *error = rc;
259 317
260int toxav_stop_call ( ToxAv *av, int32_t call_index ) 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 331
273 ToxAvCall *call = &av->calls[call_index]; 332 TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK;
274 333
275 pthread_mutex_lock(call->mutex); 334 if (m_friend_exists(av->m, friend_number) == 0) {
276 335 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND;
277 if (call->active) { 336 goto END;
278 pthread_mutex_unlock(call->mutex);
279 LOGGER_ERROR("Error while starting RTP session: call already active!\n");
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;
360
361 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
321 362
322 call->cs->acb.first = av->acb.first; 363 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
323 call->cs->acb.second = av->acb.second; 364 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
324 365
325 call->cs->vcb.first = av->vcb.first; 366 if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0)
326 call->cs->vcb.second = av->vcb.second; 367 rc = TOXAV_ERR_ANSWER_SYNC;
327 368
369END:
370 pthread_mutex_unlock(av->mutex);
328 371
329 call->crtps[audio_index] = 372 if (error)
330 rtp_new(msi_TypeAudio, av->messenger, av->msi_session->calls[call_index]->peers[0]); 373 *error = rc;
331 374
332 if ( !call->crtps[audio_index] ) { 375 return rc == TOXAV_ERR_ANSWER_OK;
333 LOGGER_ERROR("Error while starting audio RTP session!\n"); 376}
334 goto error; 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 }
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 }
346 } 438 }
439 break;
440
441 case TOXAV_CALL_CONTROL_CANCEL: {
442 /* Hang up */
443 pthread_mutex_lock(call->mutex);
444 if (msi_hangup(call->msi_call) != 0) {
445 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
446 pthread_mutex_unlock(call->mutex);
447 goto END;
448 }
449
450 call->msi_call = NULL;
451 pthread_mutex_unlock(call->mutex);
347 452
348 call->crtps[video_index]->cs = call->cs; 453 /* No mather the case, terminate the call */
454 call_kill_transmission(call);
455 call_remove(call);
456 }
457 break;
458
459 case TOXAV_CALL_CONTROL_MUTE_AUDIO: {
460 if (call->msi_call->self_capabilities & msi_CapRAudio) {
461 if (msi_change_capabilities(call->msi_call, call->
462 msi_call->self_capabilities ^ msi_CapRAudio) == -1) {
463 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
464 goto END;
465 }
466
467 rtp_stop_receiving(call->audio.first);
468 } else {
469 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
470 goto END;
471 }
472 }
473 break;
474
475 case TOXAV_CALL_CONTROL_UNMUTE_AUDIO: {
476 if (call->msi_call->self_capabilities ^ msi_CapRAudio) {
477 if (msi_change_capabilities(call->msi_call, call->
478 msi_call->self_capabilities | msi_CapRAudio) == -1) {
479 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
480 goto END;
481 }
482
483 rtp_allow_receiving(call->audio.first);
484 } else {
485 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
486 goto END;
487 }
488 }
489 break;
490
491 case TOXAV_CALL_CONTROL_HIDE_VIDEO: {
492 if (call->msi_call->self_capabilities & msi_CapRVideo) {
493 if (msi_change_capabilities(call->msi_call, call->
494 msi_call->self_capabilities ^ msi_CapRVideo) == -1) {
495 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
496 goto END;
497 }
498
499 rtp_stop_receiving(call->video.first);
500 } else {
501 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
502 goto END;
503 }
504 }
505 break;
506
507 case TOXAV_CALL_CONTROL_SHOW_VIDEO: {
508 if (call->msi_call->self_capabilities ^ msi_CapRVideo) {
509 if (msi_change_capabilities(call->msi_call, call->
510 msi_call->self_capabilities | msi_CapRVideo) == -1) {
511 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
512 goto END;
513 }
514
515 rtp_allow_receiving(call->audio.first);
516 } else {
517 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
518 goto END;
519 }
520 }
521 break;
349 } 522 }
350 523
351 call->active = 1; 524END:
352 pthread_mutex_unlock(call->mutex); 525 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 526
366 pthread_mutex_unlock(call->mutex); 527 if (error)
367 return av_ErrorCreatingRtpSessions; 528 *error = rc;
368}
369 529
370int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) 530 return rc == TOXAV_ERR_CALL_CONTROL_OK;
531}
532bool toxav_bit_rate_set(ToxAV *av, uint32_t friend_number, int32_t audio_bit_rate,
533 int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error)
371{ 534{
372 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 535 TOXAV_ERR_BIT_RATE_SET rc = TOXAV_ERR_BIT_RATE_SET_OK;
373 LOGGER_WARNING("Invalid call index: %d", call_index); 536 ToxAVCall *call;
374 return av_ErrorNoCall; 537
538 if (m_friend_exists(av->m, friend_number) == 0) {
539 rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND;
540 goto END;
375 } 541 }
376 542
377 ToxAvCall *call = &av->calls[call_index]; 543 if (audio_bit_rate > 0 && audio_bit_rate_invalid(audio_bit_rate)) {
544 rc = TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE;
545 goto END;
546 }
378 547
379 pthread_mutex_lock(call->mutex); 548 if (video_bit_rate > 0 && video_bit_rate_invalid(video_bit_rate)) {
549 rc = TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE;
550 goto END;
551 }
380 552
381 if (!call->active) { 553 pthread_mutex_lock(av->mutex);
382 pthread_mutex_unlock(call->mutex); 554 call = call_get(av, friend_number);
383 LOGGER_WARNING("Action on inactive call: %d", call_index); 555
384 return av_ErrorInvalidState; 556 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
557 pthread_mutex_unlock(av->mutex);
558 rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL;
559 goto END;
385 } 560 }
386 561
387 call->active = 0; 562 if (audio_bit_rate >= 0) {
563 LOGGER_DEBUG("Setting new audio bitrate to: %d", audio_bit_rate);
564
565 if (call->audio_bit_rate == audio_bit_rate) {
566 LOGGER_DEBUG("Audio bitrate already set to: %d", audio_bit_rate);
567 } else if (audio_bit_rate == 0) {
568 LOGGER_DEBUG("Turned off audio sending");
569 if (msi_change_capabilities(call->msi_call, call->msi_call->
570 self_capabilities ^ msi_CapSAudio) != 0) {
571 pthread_mutex_unlock(av->mutex);
572 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
573 goto END;
574 }
575 /* Audio sending is turned off; notify peer */
576 call->audio_bit_rate = 0;
577 } else {
578 pthread_mutex_lock(call->mutex);
579 if (call->audio_bit_rate == 0) {
580 LOGGER_DEBUG("Turned on audio sending");
581 /* The audio has been turned off before this */
582 if (msi_change_capabilities(call->msi_call, call->
583 msi_call->self_capabilities | msi_CapSAudio) != 0) {
584 pthread_mutex_unlock(call->mutex);
585 pthread_mutex_unlock(av->mutex);
586 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
587 goto END;
588 }
589 } else
590 LOGGER_DEBUG("Set new audio bit rate %d", audio_bit_rate);
591 call->audio_bit_rate = audio_bit_rate;
592 pthread_mutex_unlock(call->mutex);
593 }
594 }
388 595
389 pthread_mutex_lock(call->mutex_encoding_audio); 596 if (video_bit_rate >= 0) {
390 pthread_mutex_unlock(call->mutex_encoding_audio); 597 LOGGER_DEBUG("Setting new video bitrate to: %d", video_bit_rate);
391 pthread_mutex_lock(call->mutex_encoding_video); 598
392 pthread_mutex_unlock(call->mutex_encoding_video); 599 if (call->video_bit_rate == video_bit_rate) {
393 pthread_mutex_lock(call->mutex_do); 600 LOGGER_DEBUG("Video bitrate already set to: %d", video_bit_rate);
394 pthread_mutex_unlock(call->mutex_do); 601 } else if (video_bit_rate == 0) {
602 LOGGER_DEBUG("Turned off video sending");
603 /* Video sending is turned off; notify peer */
604 if (msi_change_capabilities(call->msi_call, call->msi_call->
605 self_capabilities ^ msi_CapSVideo) != 0) {
606 pthread_mutex_unlock(av->mutex);
607 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
608 goto END;
609 }
610 call->video_bit_rate = 0;
611 } else {
612 pthread_mutex_lock(call->mutex);
613 if (call->video_bit_rate == 0) {
614 LOGGER_DEBUG("Turned on video sending");
615 /* The video has been turned off before this */
616 if (msi_change_capabilities(call->msi_call, call->
617 msi_call->self_capabilities | msi_CapSVideo) != 0) {
618 pthread_mutex_unlock(call->mutex);
619 pthread_mutex_unlock(av->mutex);
620 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
621 goto END;
622 }
623 } else
624 LOGGER_DEBUG("Set new video bit rate %d", video_bit_rate);
625 call->video_bit_rate = video_bit_rate;
626 pthread_mutex_unlock(call->mutex);
627 }
628 }
629
630 pthread_mutex_unlock(av->mutex);
631END:
632 if (error)
633 *error = rc;
395 634
396 rtp_kill(call->crtps[audio_index], av->messenger); 635 return rc == TOXAV_ERR_BIT_RATE_SET_OK;
397 call->crtps[audio_index] = NULL; 636}
398 rtp_kill(call->crtps[video_index], av->messenger); 637void toxav_callback_bit_rate_status(ToxAV *av, toxav_bit_rate_status_cb *function, void *user_data)
399 call->crtps[video_index] = NULL; 638{
400 cs_kill(call->cs); 639 pthread_mutex_lock(av->mutex);
401 call->cs = NULL; 640 av->bcb.first = function;
641 av->bcb.second = user_data;
642 pthread_mutex_unlock(av->mutex);
643}
644bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pcm, size_t sample_count,
645 uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME *error)
646{
647 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
648 ToxAVCall *call;
402 649
403 pthread_mutex_destroy(call->mutex_encoding_audio); 650 if (m_friend_exists(av->m, friend_number) == 0) {
404 pthread_mutex_destroy(call->mutex_encoding_video); 651 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
405 pthread_mutex_destroy(call->mutex_do); 652 goto END;
653 }
406 654
407 pthread_mutex_unlock(call->mutex); 655 if (pthread_mutex_trylock(av->mutex) != 0) {
656 rc = TOXAV_ERR_SEND_FRAME_SYNC;
657 goto END;
658 }
659
660 call = call_get(av, friend_number);
408 661
409 return av_ErrorNone; 662 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
410} 663 pthread_mutex_unlock(av->mutex);
664 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
665 goto END;
666 }
411 667
412static int toxav_send_rtp_payload(ToxAv *av, 668 if (call->audio_bit_rate == 0 ||
413 ToxAvCall *call, 669 !(call->msi_call->self_capabilities & msi_CapSAudio) ||
414 ToxAvCallType type, 670 !(call->msi_call->peer_capabilities & msi_CapRAudio)) {
415 const uint8_t *payload, 671 pthread_mutex_unlock(av->mutex);
416 unsigned int length) 672 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
417{ 673 goto END;
418 if (call->crtps[type - av_TypeAudio]) { 674 }
419 675
420 /* Audio */ 676 pthread_mutex_lock(call->mutex_audio);
421 if (type == av_TypeAudio) 677 pthread_mutex_unlock(av->mutex);
422 return rtp_send_msg(call->crtps[audio_index], av->messenger, payload, length);
423 678
424 /* Video */ 679 if (pcm == NULL) {
425 int parts = cs_split_video_payload(call->cs, payload, length); 680 pthread_mutex_unlock(call->mutex_audio);
681 rc = TOXAV_ERR_SEND_FRAME_NULL;
682 goto END;
683 }
426 684
427 if (parts < 0) return parts; 685 if (channels > 2) {
686 pthread_mutex_unlock(call->mutex_audio);
687 rc = TOXAV_ERR_SEND_FRAME_INVALID;
688 goto END;
689 }
428 690
429 uint16_t part_size; 691 { /* Encode and send */
430 const uint8_t *iter; 692 if (ac_reconfigure_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) {
693 pthread_mutex_unlock(call->mutex_audio);
694 rc = TOXAV_ERR_SEND_FRAME_INVALID;
695 goto END;
696 }
431 697
432 int i; 698 uint8_t dest[sample_count + sizeof(sampling_rate)]; /* This is more than enough always */
433 699
434 for (i = 0; i < parts; i++) { 700 sampling_rate = htonl(sampling_rate);
435 iter = cs_get_split_video_frame(call->cs, &part_size); 701 memcpy(dest, &sampling_rate, sizeof(sampling_rate));
702 int vrc = opus_encode(call->audio.second->encoder, pcm, sample_count,
703 dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate));
436 704
437 if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) < 0) 705 if (vrc < 0) {
438 return av_ErrorSendingPayload; 706 LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc));
707 pthread_mutex_unlock(call->mutex_audio);
708 rc = TOXAV_ERR_SEND_FRAME_INVALID;
709 goto END;
439 } 710 }
440 711
441 return av_ErrorNone; 712 if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate)) != 0) {
713 LOGGER_WARNING("Failed to send audio packet");
714 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
715 }
716 }
442 717
443 } else return av_ErrorNoRtpSession;
444}
445 718
446int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) 719 pthread_mutex_unlock(call->mutex_audio);
720
721END:
722 if (error)
723 *error = rc;
724
725 return rc == TOXAV_ERR_SEND_FRAME_OK;
726}
727bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y,
728 const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error)
447{ 729{
448 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 730 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
449 LOGGER_WARNING("Invalid call index: %d", call_index); 731 ToxAVCall *call;
450 return av_ErrorNoCall; 732
733 if (m_friend_exists(av->m, friend_number) == 0) {
734 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
735 goto END;
451 } 736 }
452 737
738 if (pthread_mutex_trylock(av->mutex) != 0) {
739 rc = TOXAV_ERR_SEND_FRAME_SYNC;
740 goto END;
741 }
742
743 call = call_get(av, friend_number);
453 744
454 ToxAvCall *call = &av->calls[call_index]; 745 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
455 pthread_mutex_lock(call->mutex); 746 pthread_mutex_unlock(av->mutex);
747 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
748 goto END;
749 }
456 750
457 if (!call->active) { 751 if (call->video_bit_rate == 0 ||
458 pthread_mutex_unlock(call->mutex); 752 !(call->msi_call->self_capabilities & msi_CapSVideo) ||
459 LOGGER_WARNING("Action on inactive call: %d", call_index); 753 !(call->msi_call->peer_capabilities & msi_CapRVideo)) {
460 return av_ErrorInvalidState; 754 pthread_mutex_unlock(av->mutex);
755 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
756 goto END;
461 } 757 }
462 758
463 if (!(call->cs->capabilities & cs_VideoEncoding)) { 759 pthread_mutex_lock(call->mutex_video);
464 pthread_mutex_unlock(call->mutex); 760 pthread_mutex_unlock(av->mutex);
465 LOGGER_WARNING("Call doesn't support encoding video: %d", call_index); 761
466 return av_ErrorInvalidState; 762 if (y == NULL || u == NULL || v == NULL) {
763 pthread_mutex_unlock(call->mutex_video);
764 rc = TOXAV_ERR_SEND_FRAME_NULL;
765 goto END;
467 } 766 }
468 767
469 if (cs_set_video_encoder_resolution(call->cs, input->w, input->h) < 0) { 768 if (vc_reconfigure_encoder(call->video.second, call->video_bit_rate * 1000, width, height) != 0) {
470 pthread_mutex_unlock(call->mutex); 769 pthread_mutex_unlock(call->mutex_video);
471 return av_ErrorSettingVideoResolution; 770 rc = TOXAV_ERR_SEND_FRAME_INVALID;
771 goto END;
472 } 772 }
473 773
474 pthread_mutex_lock(call->mutex_encoding_video); 774 { /* Encode */
475 pthread_mutex_unlock(call->mutex); 775 vpx_image_t img;
776 img.w = img.h = img.d_w = img.d_h = 0;
777 vpx_img_alloc(&img, VPX_IMG_FMT_I420, width, height, 0);
778
779 /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes."
780 * http://fourcc.org/yuv.php#IYUV
781 */
782 memcpy(img.planes[VPX_PLANE_Y], y, width * height);
783 memcpy(img.planes[VPX_PLANE_U], u, (width / 2) * (height / 2));
784 memcpy(img.planes[VPX_PLANE_V], v, (width / 2) * (height / 2));
476 785
477 int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); 786 int vrc = vpx_codec_encode(call->video.second->encoder, &img,
787 call->video.second->frame_counter, 1, 0, MAX_ENCODE_TIME_US);
478 788
479 if ( rc != VPX_CODEC_OK) { 789 vpx_img_free(&img);
480 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); 790
481 pthread_mutex_unlock(call->mutex_encoding_video); 791 if (vrc != VPX_CODEC_OK) {
482 return av_ErrorEncodingVideo; 792 pthread_mutex_unlock(call->mutex_video);
793 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc));
794 rc = TOXAV_ERR_SEND_FRAME_INVALID;
795 goto END;
796 }
483 } 797 }
484 798
485 ++call->cs->frame_counter; 799 ++call->video.second->frame_counter;
486 800
487 vpx_codec_iter_t iter = NULL; 801 { /* Send frames */
488 const vpx_codec_cx_pkt_t *pkt; 802 vpx_codec_iter_t iter = NULL;
489 int copied = 0; 803 const vpx_codec_cx_pkt_t *pkt;
490 804
491 while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { 805 while ((pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter))) {
492 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { 806 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT &&
493 if ( copied + pkt->data.frame.sz > dest_max ) { 807 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 808
498 memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); 809 pthread_mutex_unlock(call->mutex_video);
499 copied += pkt->data.frame.sz; 810 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
811 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
812 goto END;
813 }
500 } 814 }
501 } 815 }
502 816
503 pthread_mutex_unlock(call->mutex_encoding_video); 817 pthread_mutex_unlock(call->mutex_video);
504 return copied; 818
505} 819END:
820 if (error)
821 *error = rc;
506 822
507int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) 823 return rc == TOXAV_ERR_SEND_FRAME_OK;
824}
825void toxav_callback_audio_receive_frame(ToxAV *av, toxav_audio_receive_frame_cb *function, void *user_data)
826{
827 pthread_mutex_lock(av->mutex);
828 av->acb.first = function;
829 av->acb.second = user_data;
830 pthread_mutex_unlock(av->mutex);
831}
832void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb *function, void *user_data)
508{ 833{
834 pthread_mutex_lock(av->mutex);
835 av->vcb.first = function;
836 av->vcb.second = user_data;
837 pthread_mutex_unlock(av->mutex);
838}
509 839
510 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) {
511 LOGGER_WARNING("Invalid call index: %d", call_index);
512 return av_ErrorNoCall;
513 }
514 840
515 ToxAvCall *call = &av->calls[call_index]; 841/*******************************************************************************
516 pthread_mutex_lock(call->mutex); 842 *
843 * :: Internal
844 *
845 ******************************************************************************/
846void callback_bwc(BWControler* bwc, uint32_t friend_number, float loss, void* user_data)
847{
848 /* Callback which is called when the internal measure mechanism reported packet loss.
849 * We report suggested lowered bitrate to an app. If app is sending both audio and video,
850 * we will report lowered bitrate for video only because in that case video probably
851 * takes more than 90% bandwidth. Otherwise, we report lowered bitrate on audio.
852 * The application may choose to disable video totally if the stream is too bad.
853 */
854
855 ToxAVCall* call = user_data;
856 assert(call);
857
858 LOGGER_DEBUG("Reported loss of %f%%", loss*100);
859
860 if (loss < .01f)
861 return;
862
863 pthread_mutex_lock(call->av->mutex);
864 if (!call->av->bcb.first) {
865 pthread_mutex_unlock(call->av->mutex);
866 LOGGER_WARNING("No callback to report loss on");
867 return;
868 }
869
870 if (call->video_bit_rate)
871 (*call->av->bcb.first) (call->av, friend_number, call->audio_bit_rate,
872 call->video_bit_rate - (call->video_bit_rate * loss),
873 call->av->bcb.second);
874 else if (call->audio_bit_rate)
875 (*call->av->bcb.first) (call->av, friend_number,
876 call->audio_bit_rate - (call->audio_bit_rate * loss),
877 0, call->av->bcb.second);
878
879 pthread_mutex_unlock(call->av->mutex);
880}
881int callback_invite(void *toxav_inst, MSICall *call)
882{
883 ToxAV *toxav = toxav_inst;
884 pthread_mutex_lock(toxav->mutex);
517 885
886 ToxAVCall *av_call = call_new(toxav, call->friend_number, NULL);
518 887
519 if (!call->active) { 888 if (av_call == NULL) {
520 pthread_mutex_unlock(call->mutex); 889 LOGGER_WARNING("Failed to initialize call...");
521 LOGGER_WARNING("Action on inactive call: %d", call_index); 890 pthread_mutex_unlock(toxav->mutex);
522 return av_ErrorInvalidState; 891 return -1;
523 } 892 }
524 893
525 int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size); 894 call->av_call = av_call;
526 pthread_mutex_unlock(call->mutex); 895 av_call->msi_call = call;
527 896
528 return rc; 897 if (toxav->ccb.first)
529} 898 toxav->ccb.first(toxav, call->friend_number, call->peer_capabilities & msi_CapSAudio,
899 call->peer_capabilities & msi_CapSVideo, toxav->ccb.second);
900 else {
901 /* No handler to capture the call request, send failure */
902 pthread_mutex_unlock(toxav->mutex);
903 return -1;
904 }
530 905
531int toxav_prepare_audio_frame ( ToxAv *av, 906 pthread_mutex_unlock(toxav->mutex);
532 int32_t call_index, 907 return 0;
533 uint8_t *dest, 908}
534 int dest_max, 909int callback_start(void *toxav_inst, MSICall *call)
535 const int16_t *frame,
536 int frame_size)
537{ 910{
538 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 911 ToxAV *toxav = toxav_inst;
539 LOGGER_WARNING("Action on nonexisting call: %d", call_index); 912 pthread_mutex_lock(toxav->mutex);
540 return av_ErrorNoCall;
541 }
542 913
543 ToxAvCall *call = &av->calls[call_index]; 914 ToxAVCall *av_call = call_get(toxav, call->friend_number);
544 pthread_mutex_lock(call->mutex);
545 915
546 if (!call->active) { 916 if (av_call == NULL) {
547 pthread_mutex_unlock(call->mutex); 917 /* Should this ever happen? */
548 LOGGER_WARNING("Action on inactive call: %d", call_index); 918 pthread_mutex_unlock(toxav->mutex);
549 return av_ErrorInvalidState; 919 return -1;
550 } 920 }
551 921
552 pthread_mutex_lock(call->mutex_encoding_audio); 922 if (!call_prepare_transmission(av_call)) {
553 pthread_mutex_unlock(call->mutex); 923 callback_error(toxav_inst, call);
554 int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); 924 pthread_mutex_unlock(toxav->mutex);
555 pthread_mutex_unlock(call->mutex_encoding_audio); 925 return -1;
926 }
556 927
557 if (rc < 0) { 928 if (!invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities)) {
558 LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); 929 callback_error(toxav_inst, call);
559 return av_ErrorEncodingAudio; 930 pthread_mutex_unlock(toxav->mutex);
931 return -1;
560 } 932 }
561 933
562 return rc; 934 pthread_mutex_unlock(toxav->mutex);
935 return 0;
563} 936}
564 937int callback_end(void *toxav_inst, MSICall *call)
565int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size)
566{ 938{
567 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 939 ToxAV *toxav = toxav_inst;
568 LOGGER_WARNING("Action on nonexisting call: %d", call_index); 940 pthread_mutex_lock(toxav->mutex);
569 return av_ErrorNoCall;
570 }
571
572 ToxAvCall *call = &av->calls[call_index];
573 pthread_mutex_lock(call->mutex);
574 941
942 invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_FINISHED);
575 943
576 if (!call->active) { 944 if (call->av_call) {
577 pthread_mutex_unlock(call->mutex); 945 call_kill_transmission(call->av_call);
578 LOGGER_WARNING("Action on inactive call: %d", call_index); 946 call_remove(call->av_call);
579 return av_ErrorInvalidState;
580 } 947 }
581 948
582 int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size); 949 pthread_mutex_unlock(toxav->mutex);
583 pthread_mutex_unlock(call->mutex); 950 return 0;
584 return rc;
585} 951}
586 952int callback_error(void *toxav_inst, MSICall *call)
587int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest )
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_ERROR);
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_capabilites(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 if (call->peer_capabilities & msi_CapSAudio)
973 rtp_allow_receiving(((ToxAVCall *)call->av_call)->audio.first);
974 else
975 rtp_stop_receiving(((ToxAVCall *)call->av_call)->audio.first);
612 976
613} 977 if (call->peer_capabilities & msi_CapSVideo)
978 rtp_allow_receiving(((ToxAVCall *)call->av_call)->video.first);
979 else
980 rtp_stop_receiving(((ToxAVCall *)call->av_call)->video.first);
981
982 invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities);
614 983
615int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) 984 pthread_mutex_unlock(toxav->mutex);
985 return 0;
986}
987bool audio_bit_rate_invalid(uint32_t bit_rate)
616{ 988{
617 return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (CSCapabilities) capability : 0; 989 /* Opus RFC 6716 section-2.1.1 dictates the following:
618 /* 0 is error here */ 990 * Opus supports all bit rates from 6 kbit/s to 510 kbit/s.
991 */
992 return bit_rate < 6 || bit_rate > 510;
619} 993}
620 994bool video_bit_rate_invalid(uint32_t bit_rate)
621Tox *toxav_get_tox(ToxAv *av)
622{ 995{
623 return (Tox *)av->messenger; 996 (void) bit_rate;
997 /* TODO: If anyone knows the answer to this one please fill it up */
998 return false;
624} 999}
1000bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state)
1001{
1002 if (av->scb.first)
1003 av->scb.first(av, friend_number, state, av->scb.second);
1004 else
1005 return false;
625 1006
626int toxav_get_active_count(ToxAv *av) 1007 return true;
1008}
1009ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error)
627{ 1010{
628 if (!av) return -1; 1011 /* Assumes mutex locked */
1012 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
1013 ToxAVCall *call = NULL;
629 1014
630 int rc = 0, i = 0; 1015 if (m_friend_exists(av->m, friend_number) == 0) {
1016 rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND;
1017 goto END;
1018 }
631 1019
632 for (; i < av->max_calls; i++) { 1020 if (m_get_friend_connectionstatus(av->m, friend_number) < 1) {
633 pthread_mutex_lock(av->calls[i].mutex); 1021 rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
1022 goto END;
1023 }
634 1024
635 if (av->calls[i].active) rc++; 1025 if (call_get(av, friend_number) != NULL) {
1026 rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL;
1027 goto END;
1028 }
636 1029
637 pthread_mutex_unlock(av->calls[i].mutex); 1030
1031 call = calloc(sizeof(ToxAVCall), 1);
1032
1033 if (call == NULL) {
1034 rc = TOXAV_ERR_CALL_MALLOC;
1035 goto END;
638 } 1036 }
639 1037
640 return rc; 1038 call->av = av;
641} 1039 call->friend_number = friend_number;
642 1040
643/* Create a new toxav group. 1041 if (av->calls == NULL) { /* Creating */
644 * 1042 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 1043
660/* Join a AV group (you need to have been invited first.) 1044 if (av->calls == NULL) {
661 * 1045 free(call);
662 * returns group number on success 1046 call = NULL;
663 * returns -1 on failure. 1047 rc = TOXAV_ERR_CALL_MALLOC;
664 * 1048 goto END;
665 * Audio data callback format (same as the one for toxav_add_av_groupchat()): 1049 }
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) 1050
667 * 1051 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)). 1052
669 */ 1053 } 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, 1054 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 *), 1055
672 void *userdata) 1056 if (tmp == NULL) {
1057 free(call);
1058 call = NULL;
1059 rc = TOXAV_ERR_CALL_MALLOC;
1060 goto END;
1061 }
1062
1063 av->calls = tmp;
1064
1065 /* Set fields in between to null */
1066 uint32_t i = av->calls_tail + 1;
1067
1068 for (; i < friend_number; i ++)
1069 av->calls[i] = NULL;
1070
1071 call->prev = av->calls[av->calls_tail];
1072 av->calls[av->calls_tail]->next = call;
1073
1074 av->calls_tail = friend_number;
1075
1076 } else if (av->calls_head > friend_number) { /* Inserting at front */
1077 call->next = av->calls[av->calls_head];
1078 av->calls[av->calls_head]->prev = call;
1079 av->calls_head = friend_number;
1080 }
1081
1082 av->calls[friend_number] = call;
1083
1084END:
1085
1086 if (error)
1087 *error = rc;
1088
1089 return call;
1090}
1091ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
673{ 1092{
674 Messenger *m = tox; 1093 /* Assumes mutex locked */
675 return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata); 1094 if (av->calls == NULL || av->calls_tail < friend_number)
1095 return NULL;
1096
1097 return av->calls[friend_number];
676} 1098}
1099ToxAVCall *call_remove(ToxAVCall *call)
1100{
1101 if (call == NULL)
1102 return NULL;
677 1103
678/* Send audio to the group chat. 1104 uint32_t friend_number = call->friend_number;
679 * 1105 ToxAV *av = call->av;
680 * return 0 on success. 1106
681 * return -1 on failure. 1107 ToxAVCall *prev = call->prev;
682 * 1108 ToxAVCall *next = call->next;
683 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 1109
684 * 1110 /* 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) 1111 * removed from the msi call.
686 * Valid number of channels are 1 or 2. 1112 */
687 * Valid sample rates are 8000, 12000, 16000, 24000, or 48000. 1113 if (call->msi_call) {
688 * 1114 call->msi_call->av_call = NULL;
689 * Recommended values are: samples = 960, channels = 1, sample_rate = 48000 1115 }
690 */ 1116
691int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, 1117 free(call);
692 unsigned int sample_rate) 1118
1119 if (prev)
1120 prev->next = next;
1121 else if (next)
1122 av->calls_head = next->friend_number;
1123 else goto CLEAR;
1124
1125 if (next)
1126 next->prev = prev;
1127 else if (prev)
1128 av->calls_tail = prev->friend_number;
1129 else goto CLEAR;
1130
1131 av->calls[friend_number] = NULL;
1132 return next;
1133
1134CLEAR:
1135 av->calls_head = av->calls_tail = 0;
1136 free(av->calls);
1137 av->calls = NULL;
1138
1139 return NULL;
1140}
1141bool call_prepare_transmission(ToxAVCall *call)
693{ 1142{
694 Messenger *m = tox; 1143 /* Assumes mutex locked */
695 return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate); 1144
1145 if (call == NULL)
1146 return false;
1147
1148 ToxAV *av = call->av;
1149
1150 if (!av->acb.first && !av->vcb.first)
1151 /* It makes no sense to have CSession without callbacks */
1152 return false;
1153
1154 if (call->active) {
1155 LOGGER_WARNING("Call already active!\n");
1156 return true;
1157 }
1158
1159 if (create_recursive_mutex(call->mutex_audio) != 0)
1160 return false;
1161
1162 if (create_recursive_mutex(call->mutex_video) != 0)
1163 goto FAILURE_3;
1164
1165 if (create_recursive_mutex(call->mutex) != 0)
1166 goto FAILURE_2;
1167
1168 /* Prepare bwc */
1169 call->bwc = bwc_new(av->m, call->friend_number, callback_bwc, call);
1170
1171 { /* Prepare audio */
1172 call->audio.second = ac_new(av, call->friend_number, av->acb.first, av->acb.second);
1173
1174 if (!call->audio.second) {
1175 LOGGER_ERROR("Failed to create audio codec session");
1176 goto FAILURE;
1177 }
1178
1179 call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_number, call->bwc,
1180 call->audio.second, ac_queue_message);
1181
1182 if (!call->audio.first) {
1183 LOGGER_ERROR("Failed to create audio rtp session");;
1184 goto FAILURE;
1185 }
1186 }
1187 { /* Prepare video */
1188 call->video.second = vc_new(av, call->friend_number, av->vcb.first, av->vcb.second);
1189
1190 if (!call->video.second) {
1191 LOGGER_ERROR("Failed to create video codec session");
1192 goto FAILURE;
1193 }
1194
1195 call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_number, call->bwc,
1196 call->video.second, vc_queue_message);
1197
1198 if (!call->video.first) {
1199 LOGGER_ERROR("Failed to create video rtp session");
1200 goto FAILURE;
1201 }
1202 }
1203
1204 call->active = 1;
1205 return true;
1206
1207FAILURE:
1208 bwc_kill(call->bwc);
1209 rtp_kill(call->audio.first);
1210 ac_kill(call->audio.second);
1211 call->audio.first = NULL;
1212 call->audio.second = NULL;
1213 rtp_kill(call->video.first);
1214 vc_kill(call->video.second);
1215 call->video.first = NULL;
1216 call->video.second = NULL;
1217 pthread_mutex_destroy(call->mutex);
1218FAILURE_2:
1219 pthread_mutex_destroy(call->mutex_video);
1220FAILURE_3:
1221 pthread_mutex_destroy(call->mutex_audio);
1222 return false;
696} 1223}
1224void call_kill_transmission(ToxAVCall *call)
1225{
1226 if (call == NULL || call->active == 0)
1227 return;
1228
1229 call->active = 0;
697 1230
1231 pthread_mutex_lock(call->mutex_audio);
1232 pthread_mutex_unlock(call->mutex_audio);
1233 pthread_mutex_lock(call->mutex_video);
1234 pthread_mutex_unlock(call->mutex_video);
1235 pthread_mutex_lock(call->mutex);
1236 pthread_mutex_unlock(call->mutex);
1237
1238 bwc_kill(call->bwc);
1239
1240 rtp_kill(call->audio.first);
1241 ac_kill(call->audio.second);
1242 call->audio.first = NULL;
1243 call->audio.second = NULL;
1244
1245 rtp_kill(call->video.first);
1246 vc_kill(call->video.second);
1247 call->video.first = NULL;
1248 call->video.second = NULL;
1249
1250 pthread_mutex_destroy(call->mutex_audio);
1251 pthread_mutex_destroy(call->mutex_video);
1252 pthread_mutex_destroy(call->mutex);
1253}
diff --git a/toxav/toxav.h b/toxav/toxav.h
index 7285f45c..5c5195b3 100644
--- a/toxav/toxav.h
+++ b/toxav/toxav.h
@@ -1,285 +1,695 @@
1/** toxav.h 1/* toxav.h
2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
2 * 4 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 5 * This file is part of Tox.
4 * 6 *
5 * This file is part of Tox. 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.
6 * 11 *
7 * Tox is free software: you can redistribute it and/or modify 12 * Tox is distributed in the hope that it will be useful,
8 * it under the terms of the GNU General Public License as published by 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * the Free Software Foundation, either version 3 of the License, or 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * (at your option) any later version. 15 * GNU General Public License for more details.
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 * 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 */ 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, TOXAV_ERR_ANSWER *error);
345
346
347/*******************************************************************************
348 *
349 * :: Call state graph
350 *
351 ******************************************************************************/
352enum TOXAV_FRIEND_CALL_STATE {
353 /**
354 * Set by the AV core if an error occurred on the remote end or if friend
355 * timed out. This is the final state after which no more state
356 * transitions can occur for the call. This call state will never be triggered
357 * in combination with other call states.
358 */
359 TOXAV_FRIEND_CALL_STATE_ERROR = 1,
360 /**
361 * The call has finished. This is the final state after which no more state
362 * transitions can occur for the call. This call state will never be
363 * triggered in combination with other call states.
364 */
365 TOXAV_FRIEND_CALL_STATE_FINISHED = 2,
366 /**
367 * The flag that marks that friend is sending audio.
368 */
369 TOXAV_FRIEND_CALL_STATE_SENDING_A = 4,
370 /**
371 * The flag that marks that friend is sending video.
372 */
373 TOXAV_FRIEND_CALL_STATE_SENDING_V = 8,
374 /**
375 * The flag that marks that friend is receiving audio.
376 */
377 TOXAV_FRIEND_CALL_STATE_ACCEPTING_A = 16,
378 /**
379 * The flag that marks that friend is receiving video.
380 */
381 TOXAV_FRIEND_CALL_STATE_ACCEPTING_V = 32,
382};
222 383
223/** 384/**
224 * Encode video frame. 385 * The function type for the call_state callback.
386 *
387 * @param friend_number The friend number for which the call state changed.
388 * @param state The bitmask of the new call state which is guaranteed to be
389 * different than the previous state. The state is set to 0 when the call is
390 * paused. The bitmask represents all the activities currently performed by the
391 * friend.
225 */ 392 */
226int toxav_prepare_video_frame ( ToxAv *av, 393typedef 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 394
232/** 395/**
233 * Send encoded video packet. 396 * Set the callback for the `call_state` event. Pass NULL to unset.
397 *
234 */ 398 */
235int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size); 399void toxav_callback_call_state(ToxAV *toxAV, toxav_call_state_cb *callback, void *user_data);
400
401/*******************************************************************************
402 *
403 * :: Call control
404 *
405 ******************************************************************************/
406typedef enum TOXAV_CALL_CONTROL {
407 /**
408 * Resume a previously paused call. Only valid if the pause was caused by this
409 * client, if not, this control is ignored. Not valid before the call is accepted.
410 */
411 TOXAV_CALL_CONTROL_RESUME,
412 /**
413 * Put a call on hold. Not valid before the call is accepted.
414 */
415 TOXAV_CALL_CONTROL_PAUSE,
416 /**
417 * Reject a call if it was not answered, yet. Cancel a call after it was
418 * answered.
419 */
420 TOXAV_CALL_CONTROL_CANCEL,
421 /**
422 * Request that the friend stops sending audio. Regardless of the friend's
423 * compliance, this will cause the audio_receive_frame event to stop being
424 * triggered on receiving an audio frame from the friend.
425 */
426 TOXAV_CALL_CONTROL_MUTE_AUDIO,
427 /**
428 * Calling this control will notify client to start sending audio again.
429 */
430 TOXAV_CALL_CONTROL_UNMUTE_AUDIO,
431 /**
432 * Request that the friend stops sending video. Regardless of the friend's
433 * compliance, this will cause the video_receive_frame event to stop being
434 * triggered on receiving a video frame from the friend.
435 */
436 TOXAV_CALL_CONTROL_HIDE_VIDEO,
437 /**
438 * Calling this control will notify client to start sending video again.
439 */
440 TOXAV_CALL_CONTROL_SHOW_VIDEO,
441} TOXAV_CALL_CONTROL;
442
443typedef enum TOXAV_ERR_CALL_CONTROL {
444 /**
445 * The function returned successfully.
446 */
447 TOXAV_ERR_CALL_CONTROL_OK,
448 /**
449 * Synchronization error occurred.
450 */
451 TOXAV_ERR_CALL_CONTROL_SYNC,
452 /**
453 * The friend_number passed did not designate a valid friend.
454 */
455 TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND,
456 /**
457 * This client is currently not in a call with the friend. Before the call is
458 * answered, only CANCEL is a valid control.
459 */
460 TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL,
461 /**
462 * Happens if user tried to pause an already paused call or if trying to
463 * resume a call that is not paused.
464 */
465 TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION,
466} TOXAV_ERR_CALL_CONTROL;
236 467
237/** 468/**
238 * Encode audio frame. 469 * Sends a call control command to a friend.
470 *
471 * @param friend_number The friend number of the friend this client is in a call
472 * with.
473 * @param control The control command to send.
474 *
475 * @return true on success.
239 */ 476 */
240int toxav_prepare_audio_frame ( ToxAv *av, 477bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error);
241 int32_t call_index, 478
242 uint8_t *dest, 479
243 int dest_max, 480/*******************************************************************************
244 const int16_t *frame, 481 *
245 int frame_size); 482 * :: Controlling bit rates
483 *
484 ******************************************************************************/
485typedef enum TOXAV_ERR_BIT_RATE_SET {
486 /**
487 * The function returned successfully.
488 */
489 TOXAV_ERR_BIT_RATE_SET_OK,
490 /**
491 * Synchronization error occurred.
492 */
493 TOXAV_ERR_BIT_RATE_SET_SYNC,
494 /**
495 * The audio bit rate passed was not one of the supported values.
496 */
497 TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE,
498 /**
499 * The video bit rate passed was not one of the supported values.
500 */
501 TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE,
502 /**
503 * The friend_number passed did not designate a valid friend.
504 */
505 TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND,
506 /**
507 * This client is currently not in a call with the friend.
508 */
509 TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL,
510} TOXAV_ERR_BIT_RATE_SET;
246 511
247/** 512/**
248 * Send encoded audio frame. 513 * Set the bit rate to be used in subsequent audio/video frames.
514 *
515 * @param friend_number The friend number of the friend for which to set the
516 * bit rate.
517 * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
518 * audio sending. Set to -1 to leave unchanged.
519 * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
520 * video sending. Set to -1 to leave unchanged.
521 *
249 */ 522 */
250int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size); 523bool toxav_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, int32_t audio_bit_rate,
524 int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error);
251 525
252/** 526/**
253 * Get codec settings from the peer. These were exchanged during call initialization 527 * The function type for the bit_rate_status callback. The event is triggered
254 * or when peer send us new csettings. 528 * when the network becomes too saturated for current bit rates at which
529 * point core suggests new bit rates.
530 *
531 * @param friend_number The friend number of the friend for which to set the
532 * bit rate.
533 * @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
534 * @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
255 */ 535 */
256int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ); 536typedef void toxav_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, void *user_data);
257 537
258/** 538/**
259 * Get friend id of peer participating in conversation. 539 * Set the callback for the `bit_rate_status` event. Pass NULL to unset.
540 *
260 */ 541 */
261int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ); 542void toxav_callback_bit_rate_status(ToxAV *toxAV, toxav_bit_rate_status_cb *callback, void *user_data);
543
544
545/*******************************************************************************
546 *
547 * :: A/V sending
548 *
549 ******************************************************************************/
550typedef enum TOXAV_ERR_SEND_FRAME {
551 /**
552 * The function returned successfully.
553 */
554 TOXAV_ERR_SEND_FRAME_OK,
555 /**
556 * In case of video, one of Y, U, or V was NULL. In case of audio, the samples
557 * data pointer was NULL.
558 */
559 TOXAV_ERR_SEND_FRAME_NULL,
560 /**
561 * The friend_number passed did not designate a valid friend.
562 */
563 TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND,
564 /**
565 * This client is currently not in a call with the friend.
566 */
567 TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL,
568 /**
569 * Synchronization error occurred.
570 */
571 TOXAV_ERR_SEND_FRAME_SYNC,
572 /**
573 * One of the frame parameters was invalid. E.g. the resolution may be too
574 * small or too large, or the audio sampling rate may be unsupported.
575 */
576 TOXAV_ERR_SEND_FRAME_INVALID,
577 /**
578 * Either friend turned off audio or video receiving or we turned off sending
579 * for the said payload.
580 */
581 TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED,
582 /**
583 * Failed to push frame through rtp interface.
584 */
585 TOXAV_ERR_SEND_FRAME_RTP_FAILED,
586} TOXAV_ERR_SEND_FRAME;
262 587
263/** 588/**
264 * Get current call state. 589 * Send an audio frame to a friend.
265 */ 590 *
266ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index ); 591 * The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]...
592 * Meaning: sample 1 for channel 1, sample 1 for channel 2, ...
593 * For mono audio, this has no meaning, every sample is subsequent. For stereo,
594 * this means the expected format is LRLRLR... with samples for left and right
595 * alternating.
596 *
597 * @param friend_number The friend number of the friend to which to send an
598 * audio frame.
599 * @param pcm An array of audio samples. The size of this array must be
600 * sample_count * channels.
601 * @param sample_count Number of samples in this frame. Valid numbers here are
602 * ((sample rate) * (audio length) / 1000), where audio length can be
603 * 2.5, 5, 10, 20, 40 or 60 millseconds.
604 * @param channels Number of audio channels. Supported values are 1 and 2.
605 * @param sampling_rate Audio sampling rate used in this frame. Valid sampling
606 * rates are 8000, 12000, 16000, 24000, or 48000.
607 */
608bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
609 size_t sample_count, uint8_t channels, uint32_t sampling_rate,
610 TOXAV_ERR_SEND_FRAME *error);
267 611
268/** 612/**
269 * Is certain capability supported. Used to determine if encoding/decoding is ready. 613 * Send a video frame to a friend.
614 *
615 * Y - plane should be of size: height * width
616 * U - plane should be of size: (height/2) * (width/2)
617 * V - plane should be of size: (height/2) * (width/2)
618 *
619 * @param friend_number The friend number of the friend to which to send a video
620 * frame.
621 * @param width Width of the frame in pixels.
622 * @param height Height of the frame in pixels.
623 * @param y Y (Luminance) plane data.
624 * @param u U (Chroma) plane data.
625 * @param v V (Chroma) plane data.
626 */
627bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
628 uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
629 TOXAV_ERR_SEND_FRAME *error);
630
631
632/*******************************************************************************
633 *
634 * :: A/V receiving
635 *
636 ******************************************************************************/
637/**
638 * The function type for the audio_receive_frame callback. The callback can be
639 * called multiple times per single iteration depending on the amount of queued
640 * frames in the buffer. The received format is the same as in send function.
641 *
642 * @param friend_number The friend number of the friend who sent an audio frame.
643 * @param pcm An array of audio samples (sample_count * channels elements).
644 * @param sample_count The number of audio samples per channel in the PCM array.
645 * @param channels Number of audio channels.
646 * @param sampling_rate Sampling rate used in this frame.
647 *
270 */ 648 */
271int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ); 649typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
650 size_t sample_count, uint8_t channels, uint32_t sampling_rate,
651 void *user_data);
272 652
273/** 653/**
274 * Returns tox reference. 654 * Set the callback for the `audio_receive_frame` event. Pass NULL to unset.
655 *
275 */ 656 */
276Tox *toxav_get_tox (ToxAv *av); 657void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_cb *callback, void *user_data);
658
659/**
660 * The function type for the video_receive_frame callback.
661 *
662 * @param friend_number The friend number of the friend who sent a video frame.
663 * @param width Width of the frame in pixels.
664 * @param height Height of the frame in pixels.
665 * @param y
666 * @param u
667 * @param v Plane data.
668 * The size of plane data is derived from width and height where
669 * Y = MAX(width, abs(ystride)) * height,
670 * U = MAX(width/2, abs(ustride)) * (height/2) and
671 * V = MAX(width/2, abs(vstride)) * (height/2).
672 * @param ystride
673 * @param ustride
674 * @param vstride Strides data. Strides represent padding for each plane
675 * that may or may not be present. You must handle strides in
676 * your image processing code. Strides are negative if the
677 * image is bottom-up hence why you MUST abs() it when
678 * calculating plane buffer size.
679 */
680typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
681 uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
682 int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
277 683
278/** 684/**
279 * Returns number of active calls or -1 on error. 685 * Set the callback for the `video_receive_frame` event. Pass NULL to unset.
686 *
280 */ 687 */
281int toxav_get_active_count (ToxAv *av); 688void toxav_callback_video_receive_frame(ToxAV *toxAV, toxav_video_receive_frame_cb *callback, void *user_data);
282 689
690/**
691 * NOTE Compatibility with old toxav group calls TODO remove
692 */
283/* Create a new toxav group. 693/* Create a new toxav group.
284 * 694 *
285 * return group number on success. 695 * return group number on success.
@@ -290,7 +700,7 @@ int toxav_get_active_count (ToxAv *av);
290 * 700 *
291 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 701 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
292 */ 702 */
293int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, 703int 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); 704 unsigned int, void *), void *userdata);
295 705
296/* Join a AV group (you need to have been invited first.) 706/* Join a AV group (you need to have been invited first.)
@@ -304,7 +714,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)). 714 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
305 */ 715 */
306int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, 716int 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); 717 void (*audio_callback)(void*, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata);
308 718
309/* Send audio to the group chat. 719/* Send audio to the group chat.
310 * 720 *
@@ -325,5 +735,4 @@ int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsign
325#ifdef __cplusplus 735#ifdef __cplusplus
326} 736}
327#endif 737#endif
328 738#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..acc1852b
--- /dev/null
+++ b/toxav/video.c
@@ -0,0 +1,267 @@
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 {
223 /* Only bit rate changed */
224 cfg.rc_target_bitrate = bit_rate;
225
226 rc = vpx_codec_enc_config_set(vc->encoder, &cfg);
227
228 if (rc != VPX_CODEC_OK) {
229 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
230 return -1;
231 }
232 }
233 else
234 {
235 /* Resolution is changed, must reinitialize encoder since libvpx v1.4 doesn't support
236 * reconfiguring encoder to use resolutions greater than initially set.
237 */
238
239 LOGGER_DEBUG("Have to reinitialize vpx encoder on session %p", vc);
240
241 cfg.rc_target_bitrate = bit_rate;
242 cfg.g_w = width;
243 cfg.g_h = height;
244
245 vpx_codec_ctx_t new_c;
246
247 rc = vpx_codec_enc_init(&new_c, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
248
249 if (rc != VPX_CODEC_OK) {
250 LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
251 return -1;
252 }
253
254 rc = vpx_codec_control(&new_c, VP8E_SET_CPUUSED, 8);
255
256 if (rc != VPX_CODEC_OK) {
257 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
258 vpx_codec_destroy(&new_c);
259 return -1;
260 }
261
262 vpx_codec_destroy(vc->encoder);
263 memcpy(vc->encoder, &new_c, sizeof(new_c));
264 }
265
266 return 0;
267}
diff --git a/toxav/video.h b/toxav/video.h
new file mode 100644
index 00000000..51f34318
--- /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 887c3702..6d45077a 100644
--- a/toxcore/Messenger.c
+++ b/toxcore/Messenger.c
@@ -2245,7 +2245,7 @@ static void connection_status_cb(Messenger *m)
2245} 2245}
2246 2246
2247 2247
2248#ifdef LOGGING 2248#ifdef TOX_LOGGER
2249#define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60UL 2249#define DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS 60UL
2250static time_t lastdump = 0; 2250static time_t lastdump = 0;
2251static char IDString[crypto_box_PUBLICKEYBYTES * 2 + 1]; 2251static char IDString[crypto_box_PUBLICKEYBYTES * 2 + 1];
@@ -2321,7 +2321,7 @@ void do_messenger(Messenger *m)
2321 do_friends(m); 2321 do_friends(m);
2322 connection_status_cb(m); 2322 connection_status_cb(m);
2323 2323
2324#ifdef LOGGING 2324#ifdef TOX_LOGGER
2325 2325
2326 if (unix_time() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) { 2326 if (unix_time() > lastdump + DUMPING_CLIENTS_FRIENDS_EVERY_N_SECONDS) {
2327 2327
@@ -2420,7 +2420,7 @@ void do_messenger(Messenger *m)
2420 } 2420 }
2421 } 2421 }
2422 2422
2423#endif /* LOGGING */ 2423#endif /* TOX_LOGGER */
2424} 2424}
2425 2425
2426/* 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/util.c b/toxcore/util.c
index c320cdc4..81fa84c6 100644
--- a/toxcore/util.c
+++ b/toxcore/util.c
@@ -192,3 +192,85 @@ 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 if ((b->end + 1) % b->size == b->start) /* full */
216 rc = b->data[b->start];
217
218 b->data[b->end] = p;
219 b->end = (b->end + 1) % b->size;
220
221 if (b->end == b->start)
222 b->start = (b->start + 1) % b->size;
223
224 return rc;
225}
226bool rb_read(RingBuffer *b, void **p)
227{
228 if (b->end == b->start) { /* Empty */
229 *p = NULL;
230 return false;
231 }
232
233 *p = b->data[b->start];
234 b->start = (b->start + 1) % b->size;
235 return true;
236}
237RingBuffer *rb_new(int size)
238{
239 RingBuffer *buf = calloc(sizeof(RingBuffer), 1);
240
241 if (!buf) return NULL;
242
243 buf->size = size + 1; /* include empty elem */
244
245 if (!(buf->data = calloc(buf->size, sizeof(void *)))) {
246 free(buf);
247 return NULL;
248 }
249
250 return buf;
251}
252void rb_kill(RingBuffer *b)
253{
254 if (b) {
255 free(b->data);
256 free(b);
257 }
258}
259uint16_t rb_size(const RingBuffer* b)
260{
261 if (rb_empty(b))
262 return 0;
263
264 return
265 b->end > b->start ?
266 b->end - b->start :
267 (b->size - b->start) + b->end;
268}
269uint16_t rb_data(const RingBuffer* b, void** dest)
270{
271 uint16_t i = 0;
272 for (; i < rb_size(b); i++)
273 dest[i] = b->data[(b->start + i) % b->size];
274
275 return i;
276}
diff --git a/toxcore/util.h b/toxcore/util.h
index fde9f03c..7cf63178 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__ */