summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--auto_tests/toxav_basic_test.c891
-rw-r--r--auto_tests/toxav_many_test.c591
-rw-r--r--[-rwxr-xr-x]autogen.sh0
-rw-r--r--other/apidsl/toxav.in.h622
-rw-r--r--testing/av_test.c749
-rw-r--r--toxav/Makefile.inc6
-rw-r--r--toxav/audio.c424
-rw-r--r--toxav/audio.h89
-rw-r--r--toxav/codec.c688
-rw-r--r--toxav/codec.h176
-rw-r--r--toxav/msi.c2083
-rw-r--r--toxav/msi.h195
-rw-r--r--toxav/rtp.c709
-rw-r--r--toxav/rtp.h122
-rw-r--r--toxav/toxav.c1735
-rw-r--r--toxav/toxav.h800
-rw-r--r--toxav/video.c375
-rw-r--r--toxav/video.h113
-rw-r--r--toxcore/logger.c12
-rw-r--r--toxcore/util.c73
-rw-r--r--toxcore/util.h11
21 files changed, 6016 insertions, 4448 deletions
diff --git a/auto_tests/toxav_basic_test.c b/auto_tests/toxav_basic_test.c
index af8d91e9..a3847640 100644
--- a/auto_tests/toxav_basic_test.c
+++ b/auto_tests/toxav_basic_test.c
@@ -9,11 +9,11 @@
9#include <check.h> 9#include <check.h>
10#include <stdlib.h> 10#include <stdlib.h>
11#include <time.h> 11#include <time.h>
12#include <assert.h>
13 12
14#include <vpx/vpx_image.h> 13#include <vpx/vpx_image.h>
15 14
16#include "../toxcore/tox.h" 15#include "../toxcore/tox.h"
16#include "../toxcore/util.h"
17#include "../toxcore/logger.h" 17#include "../toxcore/logger.h"
18#include "../toxcore/crypto_core.h" 18#include "../toxcore/crypto_core.h"
19#include "../toxav/toxav.h" 19#include "../toxav/toxav.h"
@@ -28,604 +28,425 @@
28#endif 28#endif
29 29
30 30
31#define TEST_REGULAR_AV 1
32#define TEST_REGULAR_A 1
33#define TEST_REGULAR_V 1
34#define TEST_REJECT 1
35#define TEST_CANCEL 1
36#define TEST_MUTE_UNMUTE 1
31 37
32typedef enum _CallStatus {
33 none,
34 InCall,
35 Ringing,
36 Ended,
37 Rejected,
38 Canceled,
39 TimedOut
40 38
41} CallStatus; 39typedef struct {
40 bool incoming;
41 uint32_t state;
42
43} CallControl;
42 44
43typedef struct _Party {
44 CallStatus status;
45 ToxAv *av;
46 time_t *CallStarted;
47 int call_index;
48} Party;
49 45
50typedef struct _Status { 46/**
51 Party Alice; 47 * Callbacks
52 Party Bob; 48 */
53} Status; 49void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
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
91
92void callback_call_started ( void *av, int32_t call_index, void *_arg )
93{
94 Status *cast = _arg;
95
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{ 50{
108 Status *cast = _arg; 51 (void) av;
109 52 (void) friend_number;
110 if (cast->Alice.av == av) { 53 (void) audio_enabled;
111 // ... 54 (void) video_enabled;
112 } else if (cast->Bob.av == av) { 55
113 printf ( "Call Canceled for Bob!\n" ); 56 printf("Handling CALL callback\n");
114 cast->Bob.status = Canceled; 57 ((CallControl*)user_data)->incoming = true;
115 }
116} 58}
117void callback_call_rejected ( void *av, int32_t call_index, void *_arg ) 59void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
118{ 60{
119 Status *cast = _arg; 61 (void) av;
120 62 (void) friend_number;
121 printf ( "Call rejected by Bob!\n" 63
122 "Call ended for Alice!\n" ); 64 printf("Handling CALL STATE callback: %d\n", state);
123 65 ((CallControl*)user_data)->state = state;
124 /* If Bob rejects, call is ended for alice and she sends ending */
125 if (cast->Alice.av == av) {
126 cast->Alice.status = Rejected;
127 } else if (cast->Bob.av == av) {
128 //... ignor
129 }
130} 66}
131void callback_call_ended ( void *av, int32_t call_index, void *_arg ) 67void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
68 uint16_t width, uint16_t height,
69 uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a,
70 int32_t ystride, int32_t ustride, int32_t vstride, int32_t astride,
71 void *user_data)
132{ 72{
133 Status *cast = _arg; 73 (void) av;
134 74 (void) friend_number;
135 if (cast->Alice.av == av) { 75 (void) width;
136 printf ( "Call ended for Alice!\n" ); 76 (void) height;
137 cast->Alice.status = Ended; 77 (void) y;
138 } else if (cast->Bob.av == av) { 78 (void) u;
139 printf ( "Call ended for Bob!\n" ); 79 (void) v;
140 cast->Bob.status = Ended; 80 (void) ystride;
141 } 81 (void) ustride;
82 (void) vstride;
83 (void) user_data;
142} 84}
143 85void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
144void callback_peer_cs_change ( void *av, int32_t call_index, void *_arg ) 86 int16_t const *pcm,
87 size_t sample_count,
88 uint8_t channels,
89 uint32_t sampling_rate,
90 void *user_data)
145{ 91{
146 ToxAvCSettings csettings; 92 (void) av;
147 toxav_get_peer_csettings(av, call_index, 0, &csettings); 93 (void) friend_number;
148 94 (void) pcm;
149 printf("Peer changing settings to: \n" 95 (void) sample_count;
150 "Type: %u \n" 96 (void) channels;
151 "Video bitrate: %u \n" 97 (void) sampling_rate;
152 "Video height: %u \n" 98 (void) user_data;
153 "Video width: %u \n"
154 "Audio bitrate: %u \n"
155 "Audio framedur: %u \n"
156 "Audio sample rate: %u \n"
157 "Audio channels: %u \n",
158 csettings.call_type,
159 csettings.video_bitrate,
160 csettings.max_video_height,
161 csettings.max_video_width,
162 csettings.audio_bitrate,
163 csettings.audio_frame_duration,
164 csettings.audio_sample_rate,
165 csettings.audio_channels
166 );
167} 99}
168 100void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
169void callback_self_cs_change ( void *av, int32_t call_index, void *_arg )
170{ 101{
171 ToxAvCSettings csettings; 102 (void) userdata;
172 toxav_get_peer_csettings(av, call_index, 0, &csettings); 103
173 104 if (length == 7 && memcmp("gentoo", data, 7) == 0) {
174 printf("Changed settings to: \n" 105 ck_assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0);
175 "Type: %u \n"
176 "Video bitrate: %u \n"
177 "Video height: %u \n"
178 "Video width: %u \n"
179 "Audio bitrate: %u \n"
180 "Audio framedur: %u \n"
181 "Audio sample rate: %u \n"
182 "Audio channels: %u \n",
183 csettings.call_type,
184 csettings.video_bitrate,
185 csettings.max_video_height,
186 csettings.max_video_width,
187 csettings.audio_bitrate,
188 csettings.audio_frame_duration,
189 csettings.audio_sample_rate,
190 csettings.audio_channels
191 );
192}
193
194void callback_requ_timeout ( void *av, int32_t call_index, void *_arg )
195{
196 Status *cast = _arg;
197 printf("Call timed-out!\n");
198
199 if (cast->Alice.av == av) {
200 cast->Alice.status = TimedOut;
201 } else if (cast->Bob.av == av) {
202 cast->Bob.status = TimedOut;
203 } 106 }
204} 107}
205 108
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 109
212void register_callbacks(ToxAv *av, void *data) 110/**
111 * Iterate helper
112 */
113int iterate_tox(Tox* bootstrap, Tox* Alice, Tox* Bob)
213{ 114{
214 toxav_register_callstate_callback(av, callback_call_started, av_OnStart, data); 115 tox_iterate(bootstrap);
215 toxav_register_callstate_callback(av, callback_call_canceled, av_OnCancel, data); 116 tox_iterate(Alice);
216 toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data); 117 tox_iterate(Bob);
217 toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data); 118
218 toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data); 119 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} 120}
226 121
227 122
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 123
250START_TEST(test_AV_flows) 124START_TEST(test_AV_flows)
251{ 125{
126 Tox* Alice, *Bob, *bootstrap;
127 ToxAV* AliceAV, *BobAV;
128
129 CallControl AliceCC, BobCC;
130
131 {
132 TOX_ERR_NEW error;
133
134 bootstrap = tox_new(NULL, &error);
135 ck_assert(error == TOX_ERR_NEW_OK);
136
137 Alice = tox_new(NULL, &error);
138 ck_assert(error == TOX_ERR_NEW_OK);
139
140 Bob = tox_new(NULL, &error);
141 ck_assert(error == TOX_ERR_NEW_OK);
142 }
143
144 printf("Created 3 instances of Tox\n");
145 printf("Preparing network...\n");
252 long long unsigned int cur_time = time(NULL); 146 long long unsigned int cur_time = time(NULL);
253 Tox *bootstrap_node = tox_new(0, 0); 147
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; 148 uint32_t to_compare = 974536;
260 tox_callback_friend_request(Alice, accept_friend_request, &to_compare);
261 uint8_t address[TOX_ADDRESS_SIZE]; 149 uint8_t address[TOX_ADDRESS_SIZE];
150
151 tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
262 tox_self_get_address(Alice, address); 152 tox_self_get_address(Alice, address);
263 uint32_t test = tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, 0); 153
264 154
265 ck_assert_msg(test == 0, "Failed to add friend error code: %i", test); 155 ck_assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
266 156
267 uint8_t off = 1; 157 uint8_t off = 1;
268 158
269 while (1) { 159 while (1) {
270 tox_iterate(bootstrap_node); 160 iterate_tox(bootstrap, Alice, Bob);
271 tox_iterate(Alice); 161
272 tox_iterate(Bob); 162 if (tox_self_get_connection_status(bootstrap) &&
273 163 tox_self_get_connection_status(Alice) &&
274 if (tox_self_get_connection_status(bootstrap_node) && tox_self_get_connection_status(Alice) 164 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); 165 printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
278 off = 0; 166 off = 0;
279 } 167 }
280 168
281 if (tox_friend_get_connection_status(Alice, 0, 0) && tox_friend_get_connection_status(Bob, 0, 0)) 169 if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
170 tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP)
282 break; 171 break;
283 172
284 c_sleep(20); 173 c_sleep(20);
285 } 174 }
286 175
287 printf("All set after %llu seconds! Starting call...\n", time(NULL) - cur_time); 176
288 177 {
289 muhcaps = av_DefaultSettings; 178 TOXAV_ERR_NEW error;
290 muhcaps.max_video_height = muhcaps.max_video_width = 128; 179 AliceAV = toxav_new(Alice, &error);
291 180 ck_assert(error == TOXAV_ERR_NEW_OK);
292 Status status_control = { 181
293 {none, toxav_new(Alice, 1), NULL, -1}, 182 BobAV = toxav_new(Bob, &error);
294 {none, toxav_new(Bob, 1), NULL, -1}, 183 ck_assert(error == TOXAV_ERR_NEW_OK);
295 };
296
297 ck_assert_msg(status_control.Alice.av || status_control.Bob.av, "Failed to create 2 toxav instances");
298
299
300 register_callbacks(status_control.Alice.av, &status_control);
301 register_callbacks(status_control.Bob.av, &status_control);
302
303 const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000);
304 int16_t sample_payload[frame_size];
305 randombytes((uint8_t *)sample_payload, sizeof(int16_t) * frame_size);
306
307 uint8_t prepared_payload[RTP_PAYLOAD_SIZE];
308 int payload_size;
309
310 vpx_image_t *sample_image = vpx_img_alloc(NULL, VPX_IMG_FMT_I420, 128, 128, 1);
311
312 memcpy(sample_image->planes[VPX_PLANE_Y], sample_payload, 10);
313 memcpy(sample_image->planes[VPX_PLANE_U], sample_payload, 10);
314 memcpy(sample_image->planes[VPX_PLANE_V], sample_payload, 10);
315
316
317 /*************************************************************************************************
318 * Successful flows (when call starts)
319 */
320
321 /*
322 * Call with audio only on both sides. Alice calls Bob.
323 */
324
325
326 CALL_AND_START_LOOP(TypeAudio, TypeAudio) {
327 /* Both send */
328 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload,
329 1000, sample_payload, frame_size);
330
331 if ( payload_size < 0 ) {
332 ck_assert_msg ( 0, "Failed to encode payload" );
333 }
334
335 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size);
336
337 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000,
338 sample_payload, frame_size);
339
340 if ( payload_size < 0 ) {
341 ck_assert_msg ( 0, "Failed to encode payload" );
342 }
343
344 toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size);
345
346 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */
347 step++; /* This terminates the loop */
348 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index);
349 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index);
350
351 /* Call over Alice hangs up */
352 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index);
353 }
354 } 184 }
355 TERMINATE_SCOPE() 185
356 186 toxav_callback_call(AliceAV, t_toxav_call_cb, &AliceCC);
357 187 toxav_callback_call_state(AliceAV, t_toxav_call_state_cb, &AliceCC);
358 /* 188 toxav_callback_video_receive_frame(AliceAV, t_toxav_receive_video_frame_cb, &AliceCC);
359 * Call with audio on both sides and video on one side. Alice calls Bob. 189 toxav_callback_audio_receive_frame(AliceAV, t_toxav_receive_audio_frame_cb, &AliceCC);
360 */ 190
361 CALL_AND_START_LOOP(TypeAudio, TypeVideo) { 191 toxav_callback_call(BobAV, t_toxav_call_cb, &BobCC);
362 /* Both send */ 192 toxav_callback_call_state(BobAV, t_toxav_call_state_cb, &BobCC);
363 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, 193 toxav_callback_video_receive_frame(BobAV, t_toxav_receive_video_frame_cb, &BobCC);
364 1000, sample_payload, frame_size); 194 toxav_callback_audio_receive_frame(BobAV, t_toxav_receive_audio_frame_cb, &BobCC);
365 195
366 if ( payload_size < 0 ) { 196 printf("Created 2 instances of ToxAV\n");
367 ck_assert_msg ( 0, "Failed to encode payload" ); 197 printf("All set after %llu seconds!\n", time(NULL) - cur_time);
368 } 198
369 199
370 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); 200#define REGULAR_CALL_FLOW(A_BR, V_BR) \
371 201 do { \
372 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, 202 memset(&AliceCC, 0, sizeof(CallControl)); \
373 sample_payload, frame_size); 203 memset(&BobCC, 0, sizeof(CallControl)); \
374 204 \
375 if ( payload_size < 0 ) { 205 TOXAV_ERR_CALL rc; \
376 ck_assert_msg ( 0, "Failed to encode payload" ); 206 toxav_call(AliceAV, 0, A_BR, V_BR, &rc); \
377 } 207 \
378 208 if (rc != TOXAV_ERR_CALL_OK) { \
379 toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); 209 printf("toxav_call failed: %d\n", rc); \
380 210 ck_assert(0); \
381// toxav_send_video(status_control.Bob.av, status_control.Bob.call_index, sample_image); 211 } \
382 212 \
383 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ 213 \
384 step++; /* This terminates the loop */ 214 long long unsigned int start_time = time(NULL); \
385 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); 215 \
386 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); 216 \
387 217 while (BobCC.state != TOXAV_CALL_STATE_FINISHED) { \
388 /* Call over Alice hangs up */ 218 \
389 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); 219 if (BobCC.incoming) { \
390 } 220 TOXAV_ERR_ANSWER rc; \
221 toxav_answer(BobAV, 0, A_BR, V_BR, &rc); \
222 \
223 if (rc != TOXAV_ERR_ANSWER_OK) { \
224 printf("toxav_answer failed: %d\n", rc); \
225 ck_assert(0); \
226 } \
227 BobCC.incoming = false; \
228 } else { \
229 /* TODO rtp */ \
230 \
231 if (time(NULL) - start_time >= 1) { \
232 \
233 TOXAV_ERR_CALL_CONTROL rc; \
234 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); \
235 \
236 if (rc != TOXAV_ERR_CALL_CONTROL_OK) { \
237 printf("toxav_call_control failed: %d\n", rc); \
238 ck_assert(0); \
239 } \
240 } \
241 } \
242 \
243 iterate_tox(bootstrap, Alice, Bob); \
244 } \
245 printf("Success!\n");\
246 } while(0)
247
248 if (TEST_REGULAR_AV) {
249 printf("\nTrying regular call (Audio and Video)...\n");
250 REGULAR_CALL_FLOW(48, 4000);
391 } 251 }
392 TERMINATE_SCOPE() 252
393 253 if (TEST_REGULAR_A) {
394 254 printf("\nTrying regular call (Audio only)...\n");
395 /* 255 REGULAR_CALL_FLOW(48, 0);
396 * Call with audio and video on both sides. Alice calls Bob.
397 */
398 CALL_AND_START_LOOP(TypeVideo, TypeVideo) {
399 /* Both send */
400
401 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload,
402 1000, sample_payload, frame_size);
403
404 if ( payload_size < 0 ) {
405 ck_assert_msg ( 0, "Failed to encode payload" );
406 }
407
408 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size);
409
410 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000,
411 sample_payload, frame_size);
412
413 if ( payload_size < 0 ) {
414 ck_assert_msg ( 0, "Failed to encode payload" );
415 }
416
417 toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size);
418
419// toxav_send_video(status_control.Alice.av, status_control.Alice.call_index, sample_image);
420// toxav_send_video(status_control.Bob.av, status_control.Bob.call_index, sample_image);
421
422
423 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */
424 step++; /* This terminates the loop */
425 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index);
426 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index);
427
428 /* Call over Alice hangs up */
429 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index);
430 }
431 } 256 }
432 TERMINATE_SCOPE() 257
433 258 if (TEST_REGULAR_V) {
434 259 printf("\nTrying regular call (Video only)...\n");
435 uint64_t times_they_are_a_changin = time(NULL); 260 REGULAR_CALL_FLOW(0, 4000);
436 /* Media change */ 261 }
437 CALL_AND_START_LOOP(TypeAudio, TypeAudio) { 262
438 /* Both send */ 263#undef REGULAR_CALL_FLOW
439 payload_size = toxav_prepare_audio_frame(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, 264
440 1000, sample_payload, frame_size); 265 if (TEST_REJECT) { /* Alice calls; Bob rejects */
441 266 printf("\nTrying reject flow...\n");
442 if ( payload_size < 0 ) { 267
443 ck_assert_msg ( 0, "Failed to encode payload" ); 268 memset(&AliceCC, 0, sizeof(CallControl));
269 memset(&BobCC, 0, sizeof(CallControl));
270
271 {
272 TOXAV_ERR_CALL rc;
273 toxav_call(AliceAV, 0, 48, 0, &rc);
274
275 if (rc != TOXAV_ERR_CALL_OK) {
276 printf("toxav_call failed: %d\n", rc);
277 ck_assert(0);
278 }
444 } 279 }
445 280
446 toxav_send_audio(status_control.Alice.av, status_control.Alice.call_index, prepared_payload, payload_size); 281 while (!BobCC.incoming)
447 282 iterate_tox(bootstrap, Alice, Bob);
448 payload_size = toxav_prepare_audio_frame(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, 1000, 283
449 sample_payload, frame_size); 284 /* Reject */
450 285 {
451 if ( payload_size < 0 ) { 286 TOXAV_ERR_CALL_CONTROL rc;
452 ck_assert_msg ( 0, "Failed to encode payload" ); 287 toxav_call_control(BobAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
288
289 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
290 printf("toxav_call_control failed: %d\n", rc);
291 ck_assert(0);
292 }
453 } 293 }
454 294
455 toxav_send_audio(status_control.Bob.av, status_control.Bob.call_index, prepared_payload, payload_size); 295 while (AliceCC.state != TOXAV_CALL_STATE_FINISHED)
456 296 iterate_tox(bootstrap, Alice, Bob);
457 /* Wait 2 seconds and change transmission type */ 297
458 if (time(NULL) - times_they_are_a_changin > 2) { 298 printf("Success!\n");
459 times_they_are_a_changin = time(NULL); 299 }
460 muhcaps.audio_bitrate ++; 300
461 toxav_change_settings(status_control.Alice.av, status_control.Alice.call_index, &muhcaps); 301 if (TEST_CANCEL) { /* Alice calls; Alice cancels while ringing */
302 printf("\nTrying cancel (while ringing) flow...\n");
303
304 memset(&AliceCC, 0, sizeof(CallControl));
305 memset(&BobCC, 0, sizeof(CallControl));
306
307 {
308 TOXAV_ERR_CALL rc;
309 toxav_call(AliceAV, 0, 48, 0, &rc);
310
311 if (rc != TOXAV_ERR_CALL_OK) {
312 printf("toxav_call failed: %d\n", rc);
313 ck_assert(0);
314 }
462 } 315 }
463 316
464 if (time(NULL) - cur_time > 10) { /* Transmit for 10 seconds */ 317 while (!BobCC.incoming)
465 step++; /* This terminates the loop */ 318 iterate_tox(bootstrap, Alice, Bob);
466 toxav_kill_transmission(status_control.Alice.av, status_control.Alice.call_index); 319
467 toxav_kill_transmission(status_control.Bob.av, status_control.Bob.call_index); 320 /* Cancel */
468 321 {
469 /* Call over Alice hangs up */ 322 TOXAV_ERR_CALL_CONTROL rc;
470 toxav_hangup(status_control.Alice.av, status_control.Alice.call_index); 323 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
324
325 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
326 printf("toxav_call_control failed: %d\n", rc);
327 ck_assert(0);
328 }
471 } 329 }
330
331 /* Alice will not receive end state */
332 while (BobCC.state != TOXAV_CALL_STATE_FINISHED)
333 iterate_tox(bootstrap, Alice, Bob);
334
335 printf("Success!\n");
472 } 336 }
473 TERMINATE_SCOPE() 337
474 338 if (TEST_MUTE_UNMUTE) { /* Check Mute-Unmute etc */
475 339 printf("\nTrying mute functionality...\n");
476 /************************************************************************************************* 340
477 * Other flows 341 memset(&AliceCC, 0, sizeof(CallControl));
478 */ 342 memset(&BobCC, 0, sizeof(CallControl));
479 343
480 /* 344 /* Assume sending audio and video */
481 * Call and reject 345 {
482 */ 346 TOXAV_ERR_CALL rc;
483 { 347 toxav_call(AliceAV, 0, 48, 1000, &rc);
484 int step = 0; 348
485 int running = 1; 349 if (rc != TOXAV_ERR_CALL_OK) {
486 350 printf("toxav_call failed: %d\n", rc);
487 while (running) { 351 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 } 352 }
513
514 c_sleep(20);
515 } 353 }
516 354
517 printf("\n"); 355 while (!BobCC.incoming)
518 } 356 iterate_tox(bootstrap, Alice, Bob);
519 357
520 358 /* At first try all stuff while in invalid state */
521 /* 359 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL));
522 * Call and cancel 360 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
523 */ 361 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL));
524 { 362 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL));
525 int step = 0; 363 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_HIDE_VIDEO, NULL));
526 int running = 1; 364 ck_assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_SHOW_VIDEO, NULL));
527 365
528 while (running) { 366 {
529 tox_iterate(bootstrap_node); 367 TOXAV_ERR_ANSWER rc;
530 tox_iterate(Alice); 368 toxav_answer(BobAV, 0, 48, 4000, &rc);
531 tox_iterate(Bob); 369
532 370 if (rc != TOXAV_ERR_ANSWER_OK) {
533 toxav_do(status_control.Alice.av); 371 printf("toxav_answer failed: %d\n", rc);
534 toxav_do(status_control.Bob.av); 372 ck_assert(0);
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 } 373 }
559
560 c_sleep(20);
561 } 374 }
562 375
563 printf("\n"); 376 iterate_tox(bootstrap, Alice, Bob);
564 } 377
565 378 /* Pause and Resume */
566 /* 379 printf("Pause and Resume\n");
567 * Timeout 380 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL));
568 */ 381 iterate_tox(bootstrap, Alice, Bob);
569 { 382 ck_assert(BobCC.state == 0);
570 int step = 0; 383 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
571 int running = 1; 384 iterate_tox(bootstrap, Alice, Bob);
572 385 ck_assert(BobCC.state & (TOXAV_CALL_STATE_SENDING_A | TOXAV_CALL_STATE_SENDING_V));
573 while (running) { 386
574 tox_iterate(bootstrap_node); 387 /* Mute/Unmute single */
575 tox_iterate(Alice); 388 printf("Mute/Unmute single\n");
576 tox_iterate(Bob); 389 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL));
577 390 iterate_tox(bootstrap, Alice, Bob);
578 toxav_do(status_control.Alice.av); 391 ck_assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_A);
579 toxav_do(status_control.Bob.av); 392 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL));
580 393 iterate_tox(bootstrap, Alice, Bob);
581 switch ( step ) { 394 ck_assert(BobCC.state & TOXAV_CALL_STATE_RECEIVING_A);
582 case 0: 395
583 printf("Alice is calling...\n"); 396 /* Mute/Unmute both */
584 toxav_call(status_control.Alice.av, &status_control.Alice.call_index, 0, &muhcaps, 10); 397 printf("Mute/Unmute both\n");
585 step++; 398 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL));
586 break; 399 iterate_tox(bootstrap, Alice, Bob);
587 400 ck_assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_A);
588 case 1: 401 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_HIDE_VIDEO, NULL));
589 if (status_control.Alice.status == TimedOut) running = 0; 402 iterate_tox(bootstrap, Alice, Bob);
590 403 ck_assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_V);
591 break; 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_CALL_STATE_RECEIVING_A);
407 ck_assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_SHOW_VIDEO, NULL));
408 iterate_tox(bootstrap, Alice, Bob);
409 ck_assert(BobCC.state & TOXAV_CALL_STATE_RECEIVING_V);
410
411 {
412 TOXAV_ERR_CALL_CONTROL rc;
413 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
414
415 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
416 printf("toxav_call_control failed: %d\n", rc);
417 ck_assert(0);
592 } 418 }
593
594 c_sleep(20);
595 } 419 }
596 420
597 printf("\n"); 421 iterate_tox(bootstrap, Alice, Bob);
422 ck_assert(BobCC.state == TOXAV_CALL_STATE_FINISHED);
423
424 printf("Success!\n");
598 } 425 }
599 426
600 vpx_img_free(sample_image); 427 toxav_kill(BobAV);
601 toxav_kill(status_control.Alice.av); 428 toxav_kill(AliceAV);
602 toxav_kill(status_control.Bob.av);
603 tox_kill(bootstrap_node);
604 tox_kill(Alice);
605 tox_kill(Bob); 429 tox_kill(Bob);
606 430 tox_kill(Alice);
607 printf("Calls ended!\n"); 431 tox_kill(bootstrap);
432
433 printf("\nTest successful!\n");
608} 434}
609END_TEST 435END_TEST
610 436
611/*************************************************************************************************/
612
613
614/*************************************************************************************************/
615
616/*************************************************************************************************/
617
618 437
619Suite *tox_suite(void) 438Suite *tox_suite(void)
620{ 439{
621 Suite *s = suite_create("ToxAV"); 440 Suite *s = suite_create("ToxAV");
622 441
623 DEFTESTCASE_SLOW(AV_flows, 200); 442 DEFTESTCASE_SLOW(AV_flows, 200);
624
625 return s; 443 return s;
626} 444}
627int main(int argc, char *argv[]) 445int main(int argc, char *argv[])
628{ 446{
447 (void) argc;
448 (void) argv;
449
629 Suite *tox = tox_suite(); 450 Suite *tox = tox_suite();
630 SRunner *test_runner = srunner_create(tox); 451 SRunner *test_runner = srunner_create(tox);
631 452
@@ -637,6 +458,4 @@ int main(int argc, char *argv[])
637 srunner_free(test_runner); 458 srunner_free(test_runner);
638 459
639 return number_failed; 460 return number_failed;
640
641// return test_AV_flows();
642} 461}
diff --git a/auto_tests/toxav_many_test.c b/auto_tests/toxav_many_test.c
index 6017e526..761a4525 100644
--- a/auto_tests/toxav_many_test.c
+++ b/auto_tests/toxav_many_test.c
@@ -9,11 +9,13 @@
9#include <check.h> 9#include <check.h>
10#include <stdlib.h> 10#include <stdlib.h>
11#include <time.h> 11#include <time.h>
12#include <assert.h>
13 12
14#include <vpx/vpx_image.h> 13#include <vpx/vpx_image.h>
15 14
15#include "helpers.h"
16
16#include "../toxcore/tox.h" 17#include "../toxcore/tox.h"
18#include "../toxcore/util.h"
17#include "../toxcore/logger.h" 19#include "../toxcore/logger.h"
18#include "../toxcore/crypto_core.h" 20#include "../toxcore/crypto_core.h"
19#include "../toxav/toxav.h" 21#include "../toxav/toxav.h"
@@ -26,359 +28,307 @@
26#define c_sleep(x) usleep(1000*x) 28#define c_sleep(x) usleep(1000*x)
27#endif 29#endif
28 30
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
51 Party Caller;
52 Party Callee;
53} ACall;
54
55typedef struct _Status {
56 ACall calls[3]; /* Make 3 calls for this test */
57} Status;
58 31
59Status status_control; 32typedef struct {
33 bool incoming;
34 uint32_t state;
35} CallControl;
60 36
61void accept_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) 37typedef struct {
62{ 38 ToxAV* AliceAV;
63 if (length == 7 && memcmp("gentoo", data, 7) == 0) { 39 ToxAV* BobAV;
64 tox_friend_add_norequest(m, public_key, 0); 40 CallControl* AliceCC;
65 } 41 CallControl* BobCC;
66} 42 uint32_t friend_number;
43} thread_data;
67 44
68 45/**
69/******************************************************************************/ 46 * Callbacks
70void callback_recv_invite ( void *av, int32_t call_index, void *_arg ) 47 */
71{ 48void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
72 Status *cast = _arg;
73 cast->calls[call_index].Callee.status = Ringing;
74}
75void callback_recv_ringing ( void *av, int32_t call_index, void *_arg )
76{ 49{
77 Status *cast = _arg; 50 (void) av;
78 cast->calls[call_index].Caller.status = Ringing; 51 (void) audio_enabled;
52 (void) video_enabled;
53
54 printf("Handling CALL callback\n");
55 ((CallControl*)user_data)[friend_number].incoming = true;
79} 56}
80void callback_call_ended ( void *av, int32_t call_index, void *_arg ) 57void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
81{ 58{
82 Status *cast = _arg; 59 printf("Handling CALL STATE callback: %d %p\n", state, av);
83 60 ((CallControl*)user_data)[friend_number].state = state;
84 if (av == cast->calls[call_index].Caller.av)
85 cast->calls[call_index].Caller.status = Ended;
86 else
87 cast->calls[call_index].Callee.status = Ended;
88} 61}
89void callback_call_started ( void *av, int32_t call_index, void *_arg ) 62void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
63 uint16_t width, uint16_t height,
64 uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a,
65 int32_t ystride, int32_t ustride, int32_t vstride, int32_t stride,
66 void *user_data)
90{ 67{
91 Status *cast = _arg; 68 (void) av;
92 69 (void) friend_number;
93 if (av == cast->calls[call_index].Caller.av) 70 (void) width;
94 cast->calls[call_index].Caller.status = InCall; 71 (void) height;
95 else 72 (void) y;
96 cast->calls[call_index].Callee.status = InCall; 73 (void) u;
74 (void) v;
75 (void) ystride;
76 (void) ustride;
77 (void) vstride;
78 (void) user_data;
97} 79}
98void callback_call_canceled ( void *av, int32_t call_index, void *_arg ) 80void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
81 int16_t const *pcm,
82 size_t sample_count,
83 uint8_t channels,
84 uint32_t sampling_rate,
85 void *user_data)
99{ 86{
87 (void) av;
88 (void) friend_number;
89 (void) pcm;
90 (void) sample_count;
91 (void) channels;
92 (void) sampling_rate;
93 (void) user_data;
100} 94}
101void callback_call_rejected ( void *av, int32_t call_index, void *_arg ) 95void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
102{ 96{
103 Status *cast = _arg; 97 (void) userdata;
104 cast->calls[call_index].Caller.status = Rejected; 98 if (length == 7 && memcmp("gentoo", data, 7) == 0) {
105} 99 ck_assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0);
106 100 }
107void callback_requ_timeout ( void *av, int32_t call_index, void *_arg )
108{
109 ck_assert_msg(0, "No answer!");
110} 101}
111 102
112void callback_audio (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data)
113{}
114 103
115void callback_video (void *agent, int32_t call_idx, const vpx_image_t *img, void *data) 104/**
116{} 105 * Iterate helper
117 106 */
118void register_callbacks(ToxAv *av, void *data) 107ToxAV* setup_av_instance(Tox* tox, CallControl *CC)
119{ 108{
120 toxav_register_callstate_callback(av, callback_call_started, av_OnStart, data); 109 TOXAV_ERR_NEW error;
121 toxav_register_callstate_callback(av, callback_call_canceled, av_OnCancel, data); 110
122 toxav_register_callstate_callback(av, callback_call_rejected, av_OnReject, data); 111 ToxAV* av = toxav_new(tox, &error);
123 toxav_register_callstate_callback(av, callback_call_ended, av_OnEnd, data); 112 ck_assert(error == TOXAV_ERR_NEW_OK);
124 toxav_register_callstate_callback(av, callback_recv_invite, av_OnInvite, data); 113
125 toxav_register_callstate_callback(av, callback_recv_ringing, av_OnRinging, data); 114 toxav_callback_call(av, t_toxav_call_cb, CC);
126 toxav_register_callstate_callback(av, callback_requ_timeout, av_OnRequestTimeout, data); 115 toxav_callback_call_state(av, t_toxav_call_state_cb, CC);
127 116 toxav_callback_video_receive_frame(av, t_toxav_receive_video_frame_cb, CC);
128 117 toxav_callback_audio_receive_frame(av, t_toxav_receive_audio_frame_cb, CC);
129 toxav_register_audio_callback(av, callback_audio, NULL); 118
130 toxav_register_video_callback(av, callback_video, NULL); 119 return av;
131} 120}
132/*************************************************************************************************/ 121void* call_thread(void* pd)
133
134int call_running[3];
135
136void *in_thread_call (void *arg)
137{ 122{
138#define call_print(call, what, args...) printf("[%d] " what "\n", call, ##args) 123 ToxAV* AliceAV = ((thread_data*) pd)->AliceAV;
139 124 ToxAV* BobAV = ((thread_data*) pd)->BobAV;
140 ACall *this_call = arg; 125 CallControl* AliceCC = ((thread_data*) pd)->AliceCC;
141 uint64_t start = 0; 126 CallControl* BobCC = ((thread_data*) pd)->BobCC;
142 int step = 0; 127 uint32_t friend_number = ((thread_data*) pd)->friend_number;
143 int call_idx; 128
144 129
145 const int frame_size = (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000); 130 memset(AliceCC, 0, sizeof(CallControl));
146 int16_t sample_payload[frame_size]; 131 memset(BobCC, 0, sizeof(CallControl));
147 randombytes((uint8_t *)sample_payload, sizeof(int16_t) * frame_size); 132
148 133 { /* Call */
149 uint8_t prepared_payload[RTP_PAYLOAD_SIZE]; 134 TOXAV_ERR_CALL rc;
150 135 toxav_call(AliceAV, friend_number, 48, 3000, &rc);
151 register_callbacks(this_call->Caller.av, &status_control); 136
152 register_callbacks(this_call->Callee.av, arg); 137 if (rc != TOXAV_ERR_CALL_OK) {
153 138 printf("toxav_call failed: %d\n", rc);
154 /* NOTE: CALLEE WILL ALWAHYS NEED CALL_IDX == 0 */ 139 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 } 140 }
248
249 c_sleep(20);
250
251 pthread_mutex_lock(&muhmutex);
252 } 141 }
253 142
254 pthread_mutex_unlock(&muhmutex); 143 while (!BobCC->incoming)
255 call_print(call_idx, "Call ended successfully!"); 144 c_sleep(10);
145
146 { /* Answer */
147 TOXAV_ERR_ANSWER rc;
148 toxav_answer(BobAV, 0, 8, 500, &rc);
149
150 if (rc != TOXAV_ERR_ANSWER_OK) {
151 printf("toxav_answer failed: %d\n", rc);
152 ck_assert(0);
153 }
154 }
155
156 c_sleep(30);
157
158 int16_t PCM[960];
159 uint8_t video_y[800*600];
160 uint8_t video_u[800*600 / 2];
161 uint8_t video_v[800*600 / 2];
162 uint8_t video_a[800*600];
163
164 memset(PCM, 0, sizeof(PCM));
165 memset(video_y, 0, sizeof(video_y));
166 memset(video_u, 0, sizeof(video_u));
167 memset(video_v, 0, sizeof(video_v));
168 memset(video_a, 0, sizeof(video_a));
169
170 time_t start_time = time(NULL);
171 while(time(NULL) - start_time < 4) {
172 toxav_iterate(AliceAV);
173 toxav_iterate(BobAV);
174
175 toxav_audio_send_frame(AliceAV, friend_number, PCM, 960, 1, 48000, NULL);
176 toxav_audio_send_frame(BobAV, 0, PCM, 960, 1, 48000, NULL);
177
178 toxav_video_send_frame(AliceAV, friend_number, 800, 600, video_y, video_u, video_v, video_a, NULL);
179 toxav_video_send_frame(BobAV, 0, 800, 600, video_y, video_u, video_v, video_a, NULL);
180
181 c_sleep(10);
182 }
183
184 { /* Hangup */
185 TOXAV_ERR_CALL_CONTROL rc;
186 toxav_call_control(AliceAV, friend_number, TOXAV_CALL_CONTROL_CANCEL, &rc);
187
188 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
189 printf("toxav_call_control failed: %d %p %p\n", rc, AliceAV, BobAV);
190 ck_assert(0);
191 }
192 }
193
194 c_sleep(30);
195
196 printf ("Closing thread\n");
256 pthread_exit(NULL); 197 pthread_exit(NULL);
257} 198}
258 199
259 200
260
261
262
263START_TEST(test_AV_three_calls) 201START_TEST(test_AV_three_calls)
264// void test_AV_three_calls()
265{ 202{
266 long long unsigned int cur_time = time(NULL); 203 Tox* Alice, *bootstrap, *Bobs[3];
267 Tox *bootstrap_node = tox_new(0, 0); 204 ToxAV* AliceAV, *BobsAV[3];
268 Tox *caller = tox_new(0, 0); 205
269 Tox *callees[3] = { 206 CallControl AliceCC[3], BobsCC[3];
270 tox_new(0, 0), 207
271 tox_new(0, 0), 208 {
272 tox_new(0, 0), 209 TOX_ERR_NEW error;
273 }; 210
274 211 bootstrap = tox_new(NULL, &error);
275 212 ck_assert(error == TOX_ERR_NEW_OK);
276 ck_assert_msg(bootstrap_node != NULL, "Failed to create bootstrap node"); 213
277 214 Alice = tox_new(NULL, &error);
278 int i = 0; 215 ck_assert(error == TOX_ERR_NEW_OK);
279 216
280 for (; i < 3; i ++) { 217 Bobs[0] = tox_new(NULL, &error);
281 ck_assert_msg(callees[i] != NULL, "Failed to create 3 tox instances"); 218 ck_assert(error == TOX_ERR_NEW_OK);
219
220 Bobs[1] = tox_new(NULL, &error);
221 ck_assert(error == TOX_ERR_NEW_OK);
222
223 Bobs[2] = tox_new(NULL, &error);
224 ck_assert(error == TOX_ERR_NEW_OK);
282 } 225 }
283 226
284 for ( i = 0; i < 3; i ++ ) { 227 printf("Created 5 instances of Tox\n");
285 uint32_t to_compare = 974536; 228 printf("Preparing network...\n");
286 tox_callback_friend_request(callees[i], accept_friend_request, &to_compare); 229 long long unsigned int cur_time = time(NULL);
287 uint8_t address[TOX_ADDRESS_SIZE]; 230
288 tox_self_get_address(callees[i], address); 231 uint32_t to_compare = 974536;
289 232 uint8_t address[TOX_ADDRESS_SIZE];
290 uint32_t test = tox_friend_add(caller, address, (uint8_t *)"gentoo", 7, 0); 233
291 ck_assert_msg( test == i, "Failed to add friend error code: %i", test); 234 tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
292 } 235 tox_self_get_address(Alice, address);
293 236
237
238 ck_assert(tox_friend_add(Bobs[0], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
239 ck_assert(tox_friend_add(Bobs[1], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
240 ck_assert(tox_friend_add(Bobs[2], address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
241
294 uint8_t off = 1; 242 uint8_t off = 1;
295 243
296 while (1) { 244 while (1) {
297 tox_iterate(bootstrap_node); 245 tox_iterate(bootstrap);
298 tox_iterate(caller); 246 tox_iterate(Alice);
299 247 tox_iterate(Bobs[0]);
300 for (i = 0; i < 3; i ++) { 248 tox_iterate(Bobs[1]);
301 tox_iterate(callees[i]); 249 tox_iterate(Bobs[2]);
302 } 250
303 251 if (tox_self_get_connection_status(bootstrap) &&
304 252 tox_self_get_connection_status(Alice) &&
305 if (tox_self_get_connection_status(bootstrap_node) && 253 tox_self_get_connection_status(Bobs[0]) &&
306 tox_self_get_connection_status(caller) && 254 tox_self_get_connection_status(Bobs[1]) &&
307 tox_self_get_connection_status(callees[0]) && 255 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); 256 printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
311 off = 0; 257 off = 0;
312 } 258 }
313 259
314 260 if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
315 if (tox_friend_get_connection_status(caller, 0, 0) && 261 tox_friend_get_connection_status(Alice, 1, NULL) == TOX_CONNECTION_UDP &&
316 tox_friend_get_connection_status(caller, 1, 0) && 262 tox_friend_get_connection_status(Alice, 2, NULL) == TOX_CONNECTION_UDP &&
317 tox_friend_get_connection_status(caller, 2, 0) ) 263 tox_friend_get_connection_status(Bobs[0], 0, NULL) == TOX_CONNECTION_UDP &&
264 tox_friend_get_connection_status(Bobs[1], 0, NULL) == TOX_CONNECTION_UDP &&
265 tox_friend_get_connection_status(Bobs[2], 0, NULL) == TOX_CONNECTION_UDP)
318 break; 266 break;
319 267
320 c_sleep(20); 268 c_sleep(20);
321 } 269 }
322 270
323 printf("All set after %llu seconds! Starting call...\n", time(NULL) - cur_time); 271 AliceAV = setup_av_instance(Alice, AliceCC);
324 272 BobsAV[0] = setup_av_instance(Bobs[0], BobsCC + 0);
325 ToxAv *uniqcallerav = toxav_new(caller, 3); 273 BobsAV[1] = setup_av_instance(Bobs[1], BobsCC + 1);
326 274 BobsAV[2] = setup_av_instance(Bobs[2], BobsCC + 2);
327 for (i = 0; i < 3; i ++) { 275
328 status_control.calls[i].idx = i; 276 printf("Created 4 instances of ToxAV\n");
329 277 printf("All set after %llu seconds!\n", time(NULL) - cur_time);
330 status_control.calls[i].Caller.av = uniqcallerav; 278
331 status_control.calls[i].Caller.id = 0; 279 thread_data tds[3];
332 status_control.calls[i].Caller.status = none; 280 tds[0].AliceAV = AliceAV;
333 281 tds[0].BobAV = BobsAV[0];
334 status_control.calls[i].Callee.av = toxav_new(callees[i], 1); 282 tds[0].AliceCC = AliceCC + 0;
335 status_control.calls[i].Callee.id = i; 283 tds[0].BobCC = BobsCC + 0;
336 status_control.calls[i].Callee.status = none; 284 tds[0].friend_number = 0;
337 } 285
338 286 tds[1].AliceAV = AliceAV;
339 pthread_mutex_init(&muhmutex, NULL); 287 tds[1].BobAV = BobsAV[1];
340 288 tds[1].AliceCC = AliceCC + 1;
341 for ( i = 0; i < 3; i++ ) { 289 tds[1].BobCC = BobsCC + 1;
342 call_running[i] = 1; 290 tds[1].friend_number = 1;
343 pthread_create(&status_control.calls[i].tid, NULL, in_thread_call, &status_control.calls[i]); 291
344 } 292 tds[2].AliceAV = AliceAV;
345 293 tds[2].BobAV = BobsAV[2];
346 /* Now start 3 calls and they'll run for 10 s */ 294 tds[2].AliceCC = AliceCC + 2;
347 295 tds[2].BobCC = BobsCC + 2;
348 for ( i = 0; i < 3; i++ ) 296 tds[2].friend_number = 2;
349 pthread_detach(status_control.calls[i].tid); 297
350 298 pthread_t tids[3];
351 while (call_running[0] || call_running[1] || call_running[2]) { 299 (void) pthread_create(tids + 0, NULL, call_thread, tds + 0);
352 pthread_mutex_lock(&muhmutex); 300 (void) pthread_create(tids + 1, NULL, call_thread, tds + 1);
353 301 (void) pthread_create(tids + 2, NULL, call_thread, tds + 2);
354 tox_iterate(bootstrap_node); 302
355 tox_iterate(caller); 303 (void) pthread_detach(tids[0]);
356 tox_iterate(callees[0]); 304 (void) pthread_detach(tids[1]);
357 tox_iterate(callees[1]); 305 (void) pthread_detach(tids[2]);
358 tox_iterate(callees[2]); 306
359 307 time_t start_time = time(NULL);
360 for ( i = 0; i < 3; i++ ) 308 while (time(NULL) - start_time < 5) {
361 toxav_do(status_control.calls[0].Caller.av); 309 tox_iterate(Alice);
362 310 tox_iterate(Bobs[0]);
363 toxav_do(status_control.calls[0].Callee.av); 311 tox_iterate(Bobs[1]);
364 toxav_do(status_control.calls[1].Callee.av); 312 tox_iterate(Bobs[2]);
365 toxav_do(status_control.calls[2].Callee.av);
366
367 pthread_mutex_unlock(&muhmutex);
368 c_sleep(20); 313 c_sleep(20);
369 } 314 }
370 315
371 toxav_kill(status_control.calls[0].Caller.av); 316 (void) pthread_join(tids[0], NULL);
372 toxav_kill(status_control.calls[0].Callee.av); 317 (void) pthread_join(tids[1], NULL);
373 toxav_kill(status_control.calls[1].Callee.av); 318 (void) pthread_join(tids[2], NULL);
374 toxav_kill(status_control.calls[2].Callee.av); 319
375 320 printf ("Killing all instances\n");
376 tox_kill(bootstrap_node); 321 toxav_kill(BobsAV[0]);
377 tox_kill(caller); 322 toxav_kill(BobsAV[1]);
378 323 toxav_kill(BobsAV[2]);
379 for ( i = 0; i < 3; i ++) 324 toxav_kill(AliceAV);
380 tox_kill(callees[i]); 325 tox_kill(Bobs[0]);
381 326 tox_kill(Bobs[1]);
327 tox_kill(Bobs[2]);
328 tox_kill(Alice);
329 tox_kill(bootstrap);
330
331 printf("\nTest successful!\n");
382} 332}
383END_TEST 333END_TEST
384 334
@@ -399,6 +349,9 @@ Suite *tox_suite(void)
399 349
400int main(int argc, char *argv[]) 350int main(int argc, char *argv[])
401{ 351{
352 (void) argc;
353 (void) argv;
354
402 Suite *tox = tox_suite(); 355 Suite *tox = tox_suite();
403 SRunner *test_runner = srunner_create(tox); 356 SRunner *test_runner = srunner_create(tox);
404 357
@@ -410,8 +363,4 @@ int main(int argc, char *argv[])
410 srunner_free(test_runner); 363 srunner_free(test_runner);
411 364
412 return number_failed; 365 return number_failed;
413
414// test_AV_three_calls();
415
416// return 0;
417} 366}
diff --git a/autogen.sh b/autogen.sh
index 9de319ec..9de319ec 100755..100644
--- a/autogen.sh
+++ b/autogen.sh
diff --git a/other/apidsl/toxav.in.h b/other/apidsl/toxav.in.h
new file mode 100644
index 00000000..0631c827
--- /dev/null
+++ b/other/apidsl/toxav.in.h
@@ -0,0 +1,622 @@
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 synchronisation 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 */
66
67/**
68 * External Tox type.
69 */
70class tox {
71 struct this;
72}
73
74/**
75 * ToxAV.
76 */
77class toxAV {
78
79/**
80 * The ToxAV instance type. Each ToxAV instance can be bound to only one Tox
81 * instance, and Tox instance can have only one ToxAV instance. One must make
82 * sure to close ToxAV instance prior closing Tox instance otherwise undefined
83 * behaviour occurs. Upon closing of ToxAV instance, all active calls will be
84 * forcibly terminated without notifying peers.
85 *
86 */
87struct this;
88/*******************************************************************************
89 *
90 * :: API version
91 *
92 ******************************************************************************/
93/**
94 * The major version number. Incremented when the API or ABI changes in an
95 * incompatible way.
96 */
97#define TOXAV_VERSION_MAJOR 0u
98/**
99 * The minor version number. Incremented when functionality is added without
100 * breaking the API or ABI. Set to 0 when the major version number is
101 * incremented.
102 */
103#define TOXAV_VERSION_MINOR 0u
104/**
105 * The patch or revision number. Incremented when bugfixes are applied without
106 * changing any functionality or API or ABI.
107 */
108#define TOXAV_VERSION_PATCH 0u
109
110/**
111 * A macro to check at preprocessing time whether the client code is compatible
112 * with the installed version of ToxAV.
113 */
114#define TOXAV_VERSION_IS_API_COMPATIBLE(MAJOR, MINOR, PATCH) \
115 (TOXAV_VERSION_MAJOR == MAJOR && \
116 (TOXAV_VERSION_MINOR > MINOR || \
117 (TOXAV_VERSION_MINOR == MINOR && \
118 TOXAV_VERSION_PATCH >= PATCH)))
119
120/**
121 * A macro to make compilation fail if the client code is not compatible with
122 * the installed version of ToxAV.
123 */
124#define TOXAV_VERSION_REQUIRE(MAJOR, MINOR, PATCH) \
125 typedef char toxav_required_version[TOXAV_IS_COMPATIBLE(MAJOR, MINOR, PATCH) ? 1 : -1]
126
127/**
128 * A convenience macro to call ${version.is_compatible} with the currently
129 * compiling API version.
130 */
131#define TOXAV_VERSION_IS_ABI_COMPATIBLE() \
132 toxav_version_is_compatible(TOXAV_VERSION_MAJOR, TOXAV_VERSION_MINOR, TOXAV_VERSION_PATCH)
133
134
135static namespace version {
136
137 /**
138 * Return the major version number of the library. Can be used to display the
139 * ToxAV library version or to check whether the client is compatible with the
140 * dynamically linked version of ToxAV.
141 */
142 uint32_t major();
143
144 /**
145 * Return the minor version number of the library.
146 */
147 uint32_t minor();
148
149 /**
150 * Return the patch number of the library.
151 */
152 uint32_t patch();
153
154 /**
155 * Return whether the compiled library version is compatible with the passed
156 * version numbers.
157 */
158 bool is_compatible(uint32_t major, uint32_t minor, uint32_t patch);
159
160}
161/*******************************************************************************
162 *
163 * :: Creation and destruction
164 *
165 ******************************************************************************/
166/**
167 * Start new A/V session. There can only be only one session per Tox instance.
168 */
169static this new (tox::this *tox) {
170 NULL,
171 /**
172 * Memory allocation failure while trying to allocate structures required for
173 * the A/V session.
174 */
175 MALLOC,
176 /**
177 * Attempted to create a second session for the same Tox instance.
178 */
179 MULTIPLE,
180}
181/**
182 * Releases all resources associated with the A/V session.
183 *
184 * If any calls were ongoing, these will be forcibly terminated without
185 * notifying peers. After calling this function, no other functions may be
186 * called and the av pointer becomes invalid.
187 */
188void kill();
189/**
190 * Returns the Tox instance the A/V object was created for.
191 */
192tox::this *tox { get(); }
193/*******************************************************************************
194 *
195 * :: A/V event loop
196 *
197 ******************************************************************************/
198/**
199 * Returns the interval in milliseconds when the next toxav_iterate call should
200 * be. If no call is active at the moment, this function returns 200.
201 */
202const uint32_t iteration_interval();
203/**
204 * Main loop for the session. This function needs to be called in intervals of
205 * toxav_iteration_interval() milliseconds. It is best called in the separate
206 * thread from tox_iterate.
207 */
208void iterate();
209/*******************************************************************************
210 *
211 * :: Call setup
212 *
213 ******************************************************************************/
214/**
215 * Call a friend. This will start ringing the friend.
216 *
217 * It is the client's responsibility to stop ringing after a certain timeout,
218 * if such behaviour is desired. If the client does not stop ringing, the
219 * library will not stop until the friend is disconnected.
220 *
221 * @param friend_number The friend number of the friend that should be called.
222 * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
223 * audio sending.
224 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
225 * video sending.
226 */
227bool call(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate) {
228 /**
229 * A resource allocation error occurred while trying to create the structures
230 * required for the call.
231 */
232 MALLOC,
233 /**
234 * The friend number did not designate a valid friend.
235 */
236 FRIEND_NOT_FOUND,
237 /**
238 * The friend was valid, but not currently connected.
239 */
240 FRIEND_NOT_CONNECTED,
241 /**
242 * Attempted to call a friend while already in an audio or video call with
243 * them.
244 */
245 FRIEND_ALREADY_IN_CALL,
246 /**
247 * Audio or video bit rate is invalid.
248 */
249 INVALID_BIT_RATE,
250}
251event call {
252 /**
253 * The function type for the ${event call} callback.
254 *
255 * @param friend_number The friend number from which the call is incoming.
256 * @param audio_enabled True if friend is sending audio.
257 * @param video_enabled True if friend is sending video.
258 */
259 typedef void(uint32_t friend_number, bool audio_enabled, bool video_enabled);
260}
261/**
262 * Accept an incoming call.
263 *
264 * If answering fails for any reason, the call will still be pending and it is
265 * possible to try and answer it later.
266 *
267 * @param friend_number The friend number of the friend that is calling.
268 * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
269 * audio sending.
270 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
271 * video sending.
272 */
273bool answer(uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate) {
274 /**
275 * Failed to initialize codecs for call session. Note that codec initiation
276 * will fail if there is no receive callback registered for either audio or
277 * video.
278 */
279 CODEC_INITIALIZATION,
280 /**
281 * The friend number did not designate a valid friend.
282 */
283 FRIEND_NOT_FOUND,
284 /**
285 * The friend was valid, but they are not currently trying to initiate a call.
286 * This is also returned if this client is already in a call with the friend.
287 */
288 FRIEND_NOT_CALLING,
289 /**
290 * Audio or video bit rate is invalid.
291 */
292 INVALID_BIT_RATE,
293}
294/*******************************************************************************
295 *
296 * :: Call state graph
297 *
298 ******************************************************************************/
299bitmask CALL_STATE {
300 /**
301 * Set by the AV core if an error occurred on the remote end or if friend
302 * timed out. This is the final state after which no more state
303 * transitions can occur for the call. This call state will never be triggered
304 * in combination with other call states.
305 */
306 ERROR,
307 /**
308 * The call has finished. This is the final state after which no more state
309 * transitions can occur for the call. This call state will never be
310 * triggered in combination with other call states.
311 */
312 FINISHED,
313 /**
314 * The flag that marks that friend is sending audio.
315 */
316 SENDING_A,
317 /**
318 * The flag that marks that friend is sending video.
319 */
320 SENDING_V,
321 /**
322 * The flag that marks that friend is receiving audio.
323 */
324 RECEIVING_A,
325 /**
326 * The flag that marks that friend is receiving video.
327 */
328 RECEIVING_V,
329}
330event call_state {
331 /**
332 * The function type for the ${event call_state} callback.
333 *
334 * @param friend_number The friend number for which the call state changed.
335 * @param state The new call state which is guaranteed to be different than
336 * the previous state. The state is set to 0 when the call is paused.
337 */
338 typedef void(uint32_t friend_number, uint32_t state);
339}
340/*******************************************************************************
341 *
342 * :: Call control
343 *
344 ******************************************************************************/
345enum class CALL_CONTROL {
346 /**
347 * Resume a previously paused call. Only valid if the pause was caused by this
348 * client, if not, this control is ignored. Not valid before the call is accepted.
349 */
350 RESUME,
351 /**
352 * Put a call on hold. Not valid before the call is accepted.
353 */
354 PAUSE,
355 /**
356 * Reject a call if it was not answered, yet. Cancel a call after it was
357 * answered.
358 */
359 CANCEL,
360 /**
361 * Request that the friend stops sending audio. Regardless of the friend's
362 * compliance, this will cause the ${event audio.receive_frame} event to stop being
363 * triggered on receiving an audio frame from the friend.
364 */
365 MUTE_AUDIO,
366 /**
367 * Calling this control will notify client to start sending audio again.
368 */
369 UNMUTE_AUDIO,
370 /**
371 * Request that the friend stops sending video. Regardless of the friend's
372 * compliance, this will cause the ${event video.receive_frame} event to stop being
373 * triggered on receiving an video frame from the friend.
374 */
375 HIDE_VIDEO,
376 /**
377 * Calling this control will notify client to start sending video again.
378 */
379 SHOW_VIDEO,
380}
381/**
382 * Sends a call control command to a friend.
383 *
384 * @param friend_number The friend number of the friend this client is in a call
385 * with.
386 * @param control The control command to send.
387 *
388 * @return true on success.
389 */
390bool call_control (uint32_t friend_number, CALL_CONTROL control) {
391 /**
392 * The friend_number passed did not designate a valid friend.
393 */
394 FRIEND_NOT_FOUND,
395 /**
396 * This client is currently not in a call with the friend. Before the call is
397 * answered, only CANCEL is a valid control.
398 */
399 FRIEND_NOT_IN_CALL,
400 /**
401 * Happens if user tried to pause an already paused call or if trying to
402 * resume a call that is not paused.
403 */
404 INVALID_TRANSITION,
405}
406/*******************************************************************************
407 *
408 * :: Controlling bit rates
409 *
410 ******************************************************************************/
411error for set_bit_rate {
412 /**
413 * The bit rate passed was not one of the supported values.
414 */
415 INVALID,
416 /**
417 * The friend_number passed did not designate a valid friend.
418 */
419 FRIEND_NOT_FOUND,
420 /**
421 * This client is currently not in a call with the friend.
422 */
423 FRIEND_NOT_IN_CALL,
424}
425namespace audio {
426 namespace bit_rate {
427 event status {
428 /**
429 * The function type for the ${event status} callback.
430 *
431 * @param friend_number The friend number of the friend for which to set the
432 * audio bit rate.
433 * @param stable Is the stream stable enough to keep the bit rate.
434 * Upon successful, non forceful, bit rate change, this is set to
435 * true and 'bit_rate' is set to new bit rate.
436 * The stable is set to false with bit_rate set to the unstable
437 * bit rate when either current stream is unstable with said bit rate
438 * or the non forceful change failed.
439 * @param bit_rate The bit rate in Kb/sec.
440 */
441 typedef void(uint32_t friend_number, bool stable, uint32_t bit_rate);
442 }
443 /**
444 * Set the audio bit rate to be used in subsequent audio frames. If the passed
445 * bit rate is the same as the current bit rate this function will return true
446 * without calling a callback. If there is an active non forceful setup with the
447 * passed audio bit rate and the new set request is forceful, the bit rate is
448 * forcefully set and the previous non forceful request is cancelled. The active
449 * non forceful setup will be canceled in favour of new non forceful setup.
450 *
451 * @param friend_number The friend number of the friend for which to set the
452 * audio bit rate.
453 * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
454 * audio sending.
455 * @param force True if the bit rate change is forceful.
456 *
457 */
458 bool set(uint32_t friend_number, uint32_t audio_bit_rate, bool force) with error for set_bit_rate;
459 }
460}
461namespace video {
462 namespace bit_rate {
463 event status {
464 /**
465 * The function type for the ${event status} callback.
466 *
467 * @param friend_number The friend number of the friend for which to set the
468 * video bit rate.
469 * @param stable Is the stream stable enough to keep the bit rate.
470 * Upon successful, non forceful, bit rate change, this is set to
471 * true and 'bit_rate' is set to new bit rate.
472 * The stable is set to false with bit_rate set to the unstable
473 * bit rate when either current stream is unstable with said bit rate
474 * or the non forceful change failed.
475 * @param bit_rate The bit rate in Kb/sec.
476 */
477 typedef void(uint32_t friend_number, bool stable, uint32_t bit_rate);
478 }
479 /**
480 * Set the video bit rate to be used in subsequent video frames. If the passed
481 * bit rate is the same as the current bit rate this function will return true
482 * without calling a callback. If there is an active non forceful setup with the
483 * passed video bit rate and the new set request is forceful, the bit rate is
484 * forcefully set and the previous non forceful request is cancelled. The active
485 * non forceful setup will be canceled in favour of new non forceful setup.
486 *
487 * @param friend_number The friend number of the friend for which to set the
488 * video bit rate.
489 * @param audio_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
490 * video sending.
491 * @param force True if the bit rate change is forceful.
492 *
493 */
494 bool set(uint32_t friend_number, uint32_t audio_bit_rate, bool force) with error for set_bit_rate;
495 }
496}
497/*******************************************************************************
498 *
499 * :: A/V sending
500 *
501 ******************************************************************************/
502error for send_frame {
503 /**
504 * In case of video, one of Y, U, or V was NULL. In case of audio, the samples
505 * data pointer was NULL.
506 */
507 NULL,
508 /**
509 * The friend_number passed did not designate a valid friend.
510 */
511 FRIEND_NOT_FOUND,
512 /**
513 * This client is currently not in a call with the friend.
514 */
515 FRIEND_NOT_IN_CALL,
516 /**
517 * One of the frame parameters was invalid. E.g. the resolution may be too
518 * small or too large, or the audio sampling rate may be unsupported.
519 */
520 INVALID,
521 /**
522 * Failed to push frame through rtp interface.
523 */
524 RTP_FAILED,
525}
526namespace audio {
527 /**
528 * Send an audio frame to a friend.
529 *
530 * The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]...
531 * Meaning: sample 1 for channel 1, sample 1 for channel 2, ...
532 * For mono audio, this has no meaning, every sample is subsequent. For stereo,
533 * this means the expected format is LRLRLR... with samples for left and right
534 * alternating.
535 *
536 * @param friend_number The friend number of the friend to which to send an
537 * audio frame.
538 * @param pcm An array of audio samples. The size of this array must be
539 * sample_count * channels.
540 * @param sample_count Number of samples in this frame. Valid numbers here are
541 * ((sample rate) * (audio length) / 1000), where audio length can be
542 * 2.5, 5, 10, 20, 40 or 60 millseconds.
543 * @param channels Number of audio channels. Supported values are 1 and 2.
544 * @param sampling_rate Audio sampling rate used in this frame. Valid sampling
545 * rates are 8000, 12000, 16000, 24000, or 48000.
546 */
547 bool send_frame(uint32_t friend_number, const int16_t *pcm, size_t sample_count,
548 uint8_t channels, uint32_t sampling_rate) with error for send_frame;
549}
550namespace video {
551 /**
552 * Send a video frame to a friend.
553 *
554 * Y - plane should be of size: height * width
555 * U - plane should be of size: (height/2) * (width/2)
556 * V - plane should be of size: (height/2) * (width/2)
557 *
558 * @param friend_number The friend number of the friend to which to send a video
559 * frame.
560 * @param width Width of the frame in pixels.
561 * @param height Height of the frame in pixels.
562 * @param y Y (Luminance) plane data.
563 * @param u U (Chroma) plane data.
564 * @param v V (Chroma) plane data.
565 */
566 bool send_frame(uint32_t friend_number, uint16_t width, uint16_t height,
567 const uint8_t *y, const uint8_t *u, const uint8_t *v) with error for send_frame;
568}
569/*******************************************************************************
570 *
571 * :: A/V receiving
572 *
573 ******************************************************************************/
574namespace audio {
575 event receive_frame {
576 /**
577 * The function type for the ${event receive_frame} callback.
578 *
579 * @param friend_number The friend number of the friend who sent an audio frame.
580 * @param pcm An array of audio samples (sample_count * channels elements).
581 * @param sample_count The number of audio samples per channel in the PCM array.
582 * @param channels Number of audio channels.
583 * @param sampling_rate Sampling rate used in this frame.
584 *
585 */
586 typedef void(uint32_t friend_number, const int16_t *pcm, size_t sample_count,
587 uint8_t channels, uint32_t sampling_rate);
588 }
589}
590namespace video {
591 event receive_frame {
592 /**
593 * The function type for the ${event receive_frame} callback.
594 *
595 * @param friend_number The friend number of the friend who sent a video frame.
596 * @param width Width of the frame in pixels.
597 * @param height Height of the frame in pixels.
598 * @param y
599 * @param u
600 * @param v Plane data.
601 * The size of plane data is derived from width and height where
602 * Y = MAX(width, abs(ystride)) * height,
603 * U = MAX(width/2, abs(ustride)) * (height/2) and
604 * V = MAX(width/2, abs(vstride)) * (height/2).
605 * @param ystride
606 * @param ustride
607 * @param vstride Strides data. Strides represent padding for each plane
608 * that may or may not be present. You must handle strides in
609 * your image processing code. Strides are negative if the
610 * image is bottom-up hence why you MUST abs() it when
611 * calculating plane buffer size.
612 */
613 typedef void(uint32_t friend_number, uint16_t width, uint16_t height,
614 const uint8_t *y, const uint8_t *u, const uint8_t *v,
615 int32_t ystride, int32_t ustride, int32_t vstride);
616 }
617}
618
619}
620%{
621#endif
622%}
diff --git a/testing/av_test.c b/testing/av_test.c
new file mode 100644
index 00000000..de973d91
--- /dev/null
+++ b/testing/av_test.c
@@ -0,0 +1,749 @@
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#define LOGGING
32#include "../toxcore/logger.h"
33
34/* Playing audio data */
35#include <portaudio.h>
36/* Reading audio */
37#include <sndfile.h>
38
39/* Reading and Displaying video data */
40#include <opencv/cv.h>
41#include <opencv/highgui.h>
42#include <opencv/cvwimage.h>
43
44#include <sys/stat.h>
45#include <assert.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <time.h>
49#include <string.h>
50#include <errno.h>
51#include <unistd.h>
52
53#define c_sleep(x) usleep(1000*x)
54
55
56#define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X)
57
58// RGB -> YUV
59#define RGB2Y(R, G, B) CLIP(( ( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16)
60#define RGB2U(R, G, B) CLIP(( ( -38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128)
61#define RGB2V(R, G, B) CLIP(( ( 112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128)
62
63// YUV -> RGB
64#define C(Y) ( (Y) - 16 )
65#define D(U) ( (U) - 128 )
66#define E(V) ( (V) - 128 )
67
68#define YUV2R(Y, U, V) CLIP(( 298 * C(Y) + 409 * E(V) + 128) >> 8)
69#define YUV2G(Y, U, V) CLIP(( 298 * C(Y) - 100 * D(U) - 208 * E(V) + 128) >> 8)
70#define YUV2B(Y, U, V) CLIP(( 298 * C(Y) + 516 * D(U) + 128) >> 8)
71
72
73#define TEST_TRANSFER_A 0
74#define TEST_TRANSFER_V 1
75
76
77typedef struct {
78 bool incoming;
79 uint32_t state;
80 pthread_mutex_t arb_mutex[1];
81 RingBuffer* arb; /* Audio ring buffer */
82
83} CallControl;
84
85struct toxav_thread_data {
86 ToxAV* AliceAV;
87 ToxAV* BobAV;
88 int32_t sig;
89};
90
91const char* vdout = "AV Test"; /* Video output */
92PaStream* adout = NULL; /* Audio output */
93
94
95typedef struct {
96 uint16_t size;
97 int16_t data[];
98} frame;
99
100void* pa_write_thread (void* d)
101{
102 /* The purpose of this thread is to make sure Pa_WriteStream will not block
103 * toxav_iterate thread
104 */
105 CallControl* cc = d;
106
107 while (Pa_IsStreamActive(adout)) {
108 frame* f;
109 pthread_mutex_lock(cc->arb_mutex);
110 if (rb_read(cc->arb, (void**)&f)) {
111 pthread_mutex_unlock(cc->arb_mutex);
112 Pa_WriteStream(adout, f->data, f->size);
113 free(f);
114 } else {
115 pthread_mutex_unlock(cc->arb_mutex);
116 c_sleep(10);
117 }
118 }
119}
120
121
122/**
123 * Callbacks
124 */
125void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
126{
127 printf("Handling CALL callback\n");
128 ((CallControl*)user_data)->incoming = true;
129}
130void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
131{
132 printf("Handling CALL STATE callback: %d\n", state);
133 ((CallControl*)user_data)->state = state;
134}
135void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
136 uint16_t width, uint16_t height,
137 uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a,
138 int32_t ystride, int32_t ustride, int32_t vstride, int32_t astride,
139 void *user_data)
140{
141 ystride = abs(ystride);
142 ustride = abs(ustride);
143 vstride = abs(vstride);
144 astride = abs(astride);
145
146 uint16_t *img_data = malloc(height * width * 6);
147
148 unsigned long int i, j;
149 for (i = 0; i < height; ++i) {
150 for (j = 0; j < width; ++j) {
151 uint8_t *point = (uint8_t*) img_data + 3 * ((i * width) + j);
152 int yx = y[(i * ystride) + j];
153 int ux = u[((i / 2) * ustride) + (j / 2)];
154 int vx = v[((i / 2) * vstride) + (j / 2)];
155
156 point[0] = YUV2R(yx, ux, vx);
157 point[1] = YUV2G(yx, ux, vx);
158 point[2] = YUV2B(yx, ux, vx);
159 }
160 }
161
162
163 CvMat mat = cvMat(height, width, CV_8UC3, img_data);
164
165 CvSize sz = {.height = height, .width = width};
166
167 IplImage* header = cvCreateImageHeader(sz, 1, 3);
168 IplImage* img = cvGetImage(&mat, header);
169 cvShowImage(vdout, img);
170 free(img_data);
171}
172void t_toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
173 int16_t const *pcm,
174 size_t sample_count,
175 uint8_t channels,
176 uint32_t sampling_rate,
177 void *user_data)
178{
179 CallControl* cc = user_data;
180 frame* f = malloc(sizeof(uint16_t) + sample_count * sizeof(int16_t));
181 memcpy(f->data, pcm, sample_count * sizeof(int16_t));
182 f->size = sample_count/channels;
183
184 pthread_mutex_lock(cc->arb_mutex);
185 free(rb_write(cc->arb, f));
186 pthread_mutex_unlock(cc->arb_mutex);
187}
188void t_toxav_audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number,
189 bool stable, uint32_t bit_rate, void *user_data)
190{
191 if (stable)
192 printf ("Set new audio bit rate to: %d\n", bit_rate);
193 else
194 printf ("The network is overly saturated with audio bit rate at: %d\n", bit_rate);
195}
196void t_toxav_video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number,
197 bool stable, uint32_t bit_rate, void *user_data)
198{
199 if (stable)
200 printf ("Set new video bit rate to: %d", bit_rate);
201 else
202 printf ("The network is overly saturated with video bit rate at: %d", bit_rate);
203}
204void t_accept_friend_request_cb(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
205{
206 if (length == 7 && memcmp("gentoo", data, 7) == 0) {
207 assert(tox_friend_add_norequest(m, public_key, NULL) != (uint32_t) ~0);
208 }
209}
210
211
212/**
213 */
214void initialize_tox(Tox** bootstrap, ToxAV** AliceAV, CallControl* AliceCC, ToxAV** BobAV, CallControl* BobCC)
215{
216 Tox* Alice;
217 Tox* Bob;
218
219 struct Tox_Options opts;
220 tox_options_default(&opts);
221
222 opts.end_port = 0;
223
224 {
225 TOX_ERR_NEW error;
226
227 opts.start_port = 33445;
228 *bootstrap = tox_new(&opts, NULL, 0, &error);
229 assert(error == TOX_ERR_NEW_OK);
230
231 opts.start_port = 33455;
232 Alice = tox_new(&opts, NULL, 0, &error);
233 assert(error == TOX_ERR_NEW_OK);
234
235 opts.start_port = 33465;
236 Bob = tox_new(&opts, NULL, 0, &error);
237 assert(error == TOX_ERR_NEW_OK);
238 }
239
240 printf("Created 3 instances of Tox\n");
241 printf("Preparing network...\n");
242 long long unsigned int cur_time = time(NULL);
243
244 uint32_t to_compare = 974536;
245 uint8_t address[TOX_ADDRESS_SIZE];
246
247 tox_callback_friend_request(Alice, t_accept_friend_request_cb, &to_compare);
248 tox_self_get_address(Alice, address);
249
250
251 assert(tox_friend_add(Bob, address, (uint8_t *)"gentoo", 7, NULL) != (uint32_t) ~0);
252
253 uint8_t off = 1;
254
255 while (1) {
256 tox_iterate(*bootstrap);
257 tox_iterate(Alice);
258 tox_iterate(Bob);
259
260 if (tox_self_get_connection_status(*bootstrap) &&
261 tox_self_get_connection_status(Alice) &&
262 tox_self_get_connection_status(Bob) && off) {
263 printf("Toxes are online, took %llu seconds\n", time(NULL) - cur_time);
264 off = 0;
265 }
266
267 if (tox_friend_get_connection_status(Alice, 0, NULL) == TOX_CONNECTION_UDP &&
268 tox_friend_get_connection_status(Bob, 0, NULL) == TOX_CONNECTION_UDP)
269 break;
270
271 c_sleep(20);
272 }
273
274
275 TOXAV_ERR_NEW rc;
276 *AliceAV = toxav_new(Alice, &rc);
277 assert(rc == TOXAV_ERR_NEW_OK);
278
279 *BobAV = toxav_new(Bob, &rc);
280 assert(rc == TOXAV_ERR_NEW_OK);
281
282
283 /* Alice */
284 toxav_callback_call(*AliceAV, t_toxav_call_cb, AliceCC);
285 toxav_callback_call_state(*AliceAV, t_toxav_call_state_cb, AliceCC);
286 toxav_callback_video_receive_frame(*AliceAV, t_toxav_receive_video_frame_cb, AliceCC);
287 toxav_callback_audio_receive_frame(*AliceAV, t_toxav_receive_audio_frame_cb, AliceCC);
288 toxav_callback_video_bit_rate_status(*AliceAV, t_toxav_video_bit_rate_status_cb, AliceCC);
289 toxav_callback_audio_bit_rate_status(*AliceAV, t_toxav_audio_bit_rate_status_cb, AliceCC);
290
291 /* Bob */
292 toxav_callback_call(*BobAV, t_toxav_call_cb, BobCC);
293 toxav_callback_call_state(*BobAV, t_toxav_call_state_cb, BobCC);
294 toxav_callback_video_receive_frame(*BobAV, t_toxav_receive_video_frame_cb, BobCC);
295 toxav_callback_audio_receive_frame(*BobAV, t_toxav_receive_audio_frame_cb, BobCC);
296 toxav_callback_video_bit_rate_status(*BobAV, t_toxav_video_bit_rate_status_cb, BobCC);
297 toxav_callback_audio_bit_rate_status(*BobAV, t_toxav_audio_bit_rate_status_cb, BobCC);
298
299
300 printf("Created 2 instances of ToxAV\n");
301 printf("All set after %llu seconds!\n", time(NULL) - cur_time);
302}
303int iterate_tox(Tox* bootstrap, ToxAV* AliceAV, ToxAV* BobAV)
304{
305 tox_iterate(bootstrap);
306 tox_iterate(toxav_get_tox(AliceAV));
307 tox_iterate(toxav_get_tox(BobAV));
308
309 return MIN(tox_iteration_interval(toxav_get_tox(AliceAV)), tox_iteration_interval(toxav_get_tox(BobAV)));
310}
311void* iterate_toxav (void * data)
312{
313 struct toxav_thread_data* data_cast = data;
314#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
315 cvNamedWindow(vdout, CV_WINDOW_AUTOSIZE);
316#endif
317
318 while (data_cast->sig == 0) {
319 toxav_iterate(data_cast->AliceAV);
320 toxav_iterate(data_cast->BobAV);
321 int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV));
322
323 printf("\rIteration interval: %d ", rc);
324 fflush(stdout);
325
326#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
327 cvWaitKey(rc);
328#else
329 c_sleep(rc);
330#endif
331 }
332
333 data_cast->sig = 1;
334
335#if defined TEST_TRANSFER_V && TEST_TRANSFER_V == 1
336 cvDestroyWindow(vdout);
337#endif
338
339 pthread_exit(NULL);
340}
341
342int send_opencv_img(ToxAV* av, uint32_t friend_number, const IplImage* img)
343{
344 int32_t strides[3] = { 1280, 640, 640 };
345 uint8_t* planes[3] = {
346 malloc(img->height * img->width),
347 malloc(img->height * img->width / 2),
348 malloc(img->height * img->width / 2),
349 };
350
351 int x_chroma_shift = 1;
352 int y_chroma_shift = 1;
353
354 int x, y;
355 for (y = 0; y < img->height; ++y) {
356 for (x = 0; x < img->width; ++x) {
357 uint8_t r = img->imageData[(x + y * img->width) * 3 + 0];
358 uint8_t g = img->imageData[(x + y * img->width) * 3 + 1];
359 uint8_t b = img->imageData[(x + y * img->width) * 3 + 2];
360
361 planes[0][x + y * strides[0]] = RGB2Y(r, g, b);
362 if (!(x % (1 << x_chroma_shift)) && !(y % (1 << y_chroma_shift))) {
363 const int i = x / (1 << x_chroma_shift);
364 const int j = y / (1 << y_chroma_shift);
365 planes[1][i + j * strides[1]] = RGB2U(r, g, b);
366 planes[2][i + j * strides[2]] = RGB2V(r, g, b);
367 }
368 }
369 }
370
371
372 int rc = toxav_video_send_frame(av, friend_number, img->width, img->height, planes[0], planes[1], planes[2], NULL, NULL);
373 free(planes[0]);
374 free(planes[1]);
375 free(planes[2]);
376 return rc;
377}
378
379int print_audio_devices()
380{
381 int i = 0;
382 for (i = 0; i < Pa_GetDeviceCount(); ++i) {
383 const PaDeviceInfo* info = Pa_GetDeviceInfo(i);
384 if (info)
385 printf("%d) %s\n", i, info->name);
386 }
387
388 return 0;
389}
390
391int print_help (const char* name)
392{
393 printf("Usage: %s -[a:v:o:dh]\n"
394 "-a <path> audio input file\n"
395 "-b <ms> audio frame duration\n"
396 "-v <path> video input file\n"
397 "-x <ms> video frame duration\n"
398 "-o <idx> output audio device index\n"
399 "-d print output audio devices\n"
400 "-h print this help\n", name);
401
402 return 0;
403}
404
405
406int main (int argc, char** argv)
407{
408 freopen("/dev/zero", "w", stderr);
409 Pa_Initialize();
410
411 struct stat st;
412
413 /* AV files for testing */
414 const char* af_name = NULL;
415 const char* vf_name = NULL;
416 long audio_out_dev_idx = -1;
417
418 int32_t audio_frame_duration = 20;
419 int32_t video_frame_duration = 10;
420
421 /* Parse settings */
422 CHECK_ARG: switch (getopt(argc, argv, "a:b:v:x:o:dh")) {
423 case 'a':
424 af_name = optarg;
425 goto CHECK_ARG;
426 case 'b':{
427 char *d;
428 audio_frame_duration = strtol(optarg, &d, 10);
429 if (*d) {
430 printf("Invalid value for argument: 'b'");
431 exit(1);
432 }
433 goto CHECK_ARG;
434 }
435 case 'v':
436 vf_name = optarg;
437 goto CHECK_ARG;
438 case 'x':{
439 char *d;
440 video_frame_duration = strtol(optarg, &d, 10);
441 if (*d) {
442 printf("Invalid value for argument: 'x'");
443 exit(1);
444 }
445 goto CHECK_ARG;
446 }
447 case 'o': {
448 char *d;
449 audio_out_dev_idx = strtol(optarg, &d, 10);
450 if (*d) {
451 printf("Invalid value for argument: 'o'");
452 exit(1);
453 }
454 goto CHECK_ARG;
455 }
456 case 'd':
457 return print_audio_devices();
458 case 'h':
459 return print_help(argv[0]);
460 case '?':
461 exit(1);
462 case -1:;
463 }
464
465 { /* Check files */
466 if (!af_name) {
467 printf("Required audio input file!\n");
468 exit(1);
469 }
470
471 if (!vf_name) {
472 printf("Required video input file!\n");
473 exit(1);
474 }
475
476 /* Check for files */
477 if(stat(af_name, &st) != 0 || !S_ISREG(st.st_mode))
478 {
479 printf("%s doesn't seem to be a regular file!\n", af_name);
480 exit(1);
481 }
482
483 if(stat(vf_name, &st) != 0 || !S_ISREG(st.st_mode))
484 {
485 printf("%s doesn't seem to be a regular file!\n", vf_name);
486 exit(1);
487 }
488 }
489
490 if (audio_out_dev_idx < 0)
491 audio_out_dev_idx = Pa_GetDefaultOutputDevice();
492
493 const PaDeviceInfo* audio_dev = Pa_GetDeviceInfo(audio_out_dev_idx);
494 if (!audio_dev) {
495 fprintf(stderr, "Device under index: %ld invalid", audio_out_dev_idx);
496 return 1;
497 }
498
499 printf("Using audio device: %s\n", audio_dev->name);
500 printf("Using audio file: %s\n", af_name);
501 printf("Using video file: %s\n", vf_name);
502
503 /* START TOX NETWORK */
504
505 Tox *bootstrap;
506 ToxAV *AliceAV;
507 ToxAV *BobAV;
508
509 CallControl AliceCC;
510 CallControl BobCC;
511
512 initialize_tox(&bootstrap, &AliceAV, &AliceCC, &BobAV, &BobCC);
513
514 if (TEST_TRANSFER_A) {
515 SNDFILE* af_handle;
516 SF_INFO af_info;
517
518 printf("\nTrying audio enc/dec...\n");
519
520 memset(&AliceCC, 0, sizeof(CallControl));
521 memset(&BobCC, 0, sizeof(CallControl));
522
523 pthread_mutex_init(AliceCC.arb_mutex, NULL);
524 pthread_mutex_init(BobCC.arb_mutex, NULL);
525
526 AliceCC.arb = rb_new(16);
527 BobCC.arb = rb_new(16);
528
529 { /* Call */
530 TOXAV_ERR_CALL rc;
531 toxav_call(AliceAV, 0, 48, 0, &rc);
532
533 if (rc != TOXAV_ERR_CALL_OK) {
534 printf("toxav_call failed: %d\n", rc);
535 exit(1);
536 }
537 }
538
539 while (!BobCC.incoming)
540 iterate_tox(bootstrap, AliceAV, BobAV);
541
542 { /* Answer */
543 TOXAV_ERR_ANSWER rc;
544 toxav_answer(BobAV, 0, 48, 0, &rc);
545
546 if (rc != TOXAV_ERR_ANSWER_OK) {
547 printf("toxav_answer failed: %d\n", rc);
548 exit(1);
549 }
550 }
551
552 while (AliceCC.state == 0)
553 iterate_tox(bootstrap, AliceAV, BobAV);
554
555 /* Open audio file */
556 af_handle = sf_open(af_name, SFM_READ, &af_info);
557 if (af_handle == NULL) {
558 printf("Failed to open the file.\n");
559 exit(1);
560 }
561
562 int16_t PCM[5760];
563
564 time_t start_time = time(NULL);
565 time_t expected_time = af_info.frames / af_info.samplerate + 2;
566
567
568 /* Start decode thread */
569 struct toxav_thread_data data = {
570 .AliceAV = AliceAV,
571 .BobAV = BobAV,
572 .sig = 0
573 };
574
575 pthread_t dect;
576 pthread_create(&dect, NULL, iterate_toxav, &data);
577 pthread_detach(dect);
578
579 int frame_size = (af_info.samplerate * audio_frame_duration / 1000) * af_info.channels;
580
581 struct PaStreamParameters output;
582 output.device = audio_out_dev_idx;
583 output.channelCount = af_info.channels;
584 output.sampleFormat = paInt16;
585 output.suggestedLatency = audio_dev->defaultHighOutputLatency;
586 output.hostApiSpecificStreamInfo = NULL;
587
588 PaError err = Pa_OpenStream(&adout, NULL, &output, af_info.samplerate, frame_size, paNoFlag, NULL, NULL);
589 assert(err == paNoError);
590
591 err = Pa_StartStream(adout);
592 assert(err == paNoError);
593
594 toxav_audio_bit_rate_set(AliceAV, 0, 64, false, NULL);
595
596 /* Start write thread */
597 pthread_t t;
598 pthread_create(&t, NULL, pa_write_thread, &BobCC);
599 pthread_detach(t);
600
601 printf("Sample rate %d\n", af_info.samplerate);
602 while ( start_time + expected_time > time(NULL) ) {
603 uint64_t enc_start_time = current_time_monotonic();
604 int64_t count = sf_read_short(af_handle, PCM, frame_size);
605 if (count > 0) {
606 TOXAV_ERR_SEND_FRAME rc;
607 if (toxav_audio_send_frame(AliceAV, 0, PCM, count/af_info.channels, af_info.channels, af_info.samplerate, &rc) == false) {
608 printf("Error sending frame of size %ld: %d\n", count, rc);
609 }
610 }
611 iterate_tox(bootstrap, AliceAV, BobAV);
612 c_sleep(abs(audio_frame_duration - (current_time_monotonic() - enc_start_time) - 1));
613 }
614
615 printf("Played file in: %lu; stopping stream...\n", time(NULL) - start_time);
616
617 Pa_StopStream(adout);
618 sf_close(af_handle);
619
620 { /* Hangup */
621 TOXAV_ERR_CALL_CONTROL rc;
622 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
623
624 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
625 printf("toxav_call_control failed: %d\n", rc);
626 exit(1);
627 }
628 }
629
630 iterate_tox(bootstrap, AliceAV, BobAV);
631 assert(BobCC.state == TOXAV_CALL_STATE_FINISHED);
632
633 /* Stop decode thread */
634 data.sig = -1;
635 while(data.sig != 1)
636 pthread_yield();
637
638 pthread_mutex_destroy(AliceCC.arb_mutex);
639 pthread_mutex_destroy(BobCC.arb_mutex);
640
641 void* f = NULL;
642 while(rb_read(AliceCC.arb, &f))
643 free(f);
644
645 while(rb_read(BobCC.arb, &f))
646 free(f);
647
648 printf("Success!");
649 }
650
651 if (TEST_TRANSFER_V) {
652 printf("\nTrying video enc/dec...\n");
653
654 memset(&AliceCC, 0, sizeof(CallControl));
655 memset(&BobCC, 0, sizeof(CallControl));
656
657 { /* Call */
658 TOXAV_ERR_CALL rc;
659 toxav_call(AliceAV, 0, 0, 3000, &rc);
660
661 if (rc != TOXAV_ERR_CALL_OK) {
662 printf("toxav_call failed: %d\n", rc);
663 exit(1);
664 }
665 }
666
667 while (!BobCC.incoming)
668 iterate_tox(bootstrap, AliceAV, BobAV);
669
670 { /* Answer */
671 TOXAV_ERR_ANSWER rc;
672 toxav_answer(BobAV, 0, 0, 500, &rc);
673
674 if (rc != TOXAV_ERR_ANSWER_OK) {
675 printf("toxav_answer failed: %d\n", rc);
676 exit(1);
677 }
678 }
679
680 iterate_tox(bootstrap, AliceAV, BobAV);
681
682 /* Start decode thread */
683 struct toxav_thread_data data = {
684 .AliceAV = AliceAV,
685 .BobAV = BobAV,
686 .sig = 0
687 };
688
689 pthread_t dect;
690 pthread_create(&dect, NULL, iterate_toxav, &data);
691 pthread_detach(dect);
692
693 CvCapture* capture = cvCreateFileCapture(vf_name);
694 if (!capture) {
695 printf("Failed to open video file: %s\n", vf_name);
696 exit(1);
697 }
698
699 toxav_video_bit_rate_set(AliceAV, 0, 5000, false, NULL);
700
701 time_t start_time = time(NULL);
702 while(start_time + 90 > time(NULL)) {
703 IplImage* frame = cvQueryFrame( capture );
704 if (!frame)
705 break;
706
707 send_opencv_img(AliceAV, 0, frame);
708 iterate_tox(bootstrap, AliceAV, BobAV);
709 c_sleep(video_frame_duration);
710 }
711
712 cvReleaseCapture(&capture);
713
714 { /* Hangup */
715 TOXAV_ERR_CALL_CONTROL rc;
716 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
717
718 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
719 printf("toxav_call_control failed: %d\n", rc);
720 exit(1);
721 }
722 }
723
724 iterate_tox(bootstrap, AliceAV, BobAV);
725 assert(BobCC.state == TOXAV_CALL_STATE_FINISHED);
726
727 /* Stop decode thread */
728 printf("Stopping decode thread\n");
729 data.sig = -1;
730 while(data.sig != 1)
731 pthread_yield();
732
733 printf("Success!");
734 }
735
736
737 Tox* Alice = toxav_get_tox(AliceAV);
738 Tox* Bob = toxav_get_tox(BobAV);
739 toxav_kill(BobAV);
740 toxav_kill(AliceAV);
741 tox_kill(Bob);
742 tox_kill(Alice);
743 tox_kill(bootstrap);
744
745 printf("\nTest successful!\n");
746
747 Pa_Terminate();
748 return 0;
749}
diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc
index 0b4b869d..3907951e 100644
--- a/toxav/Makefile.inc
+++ b/toxav/Makefile.inc
@@ -10,8 +10,10 @@ libtoxav_la_SOURCES = ../toxav/rtp.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/video.h \
16 ../toxav/video.c \
15 ../toxav/toxav.h \ 17 ../toxav/toxav.h \
16 ../toxav/toxav.c 18 ../toxav/toxav.c
17 19
diff --git a/toxav/audio.c b/toxav/audio.c
new file mode 100644
index 00000000..f6993a1d
--- /dev/null
+++ b/toxav/audio.c
@@ -0,0 +1,424 @@
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#include <stdlib.h>
23
24#include "audio.h"
25#include "rtp.h"
26
27#include "../toxcore/logger.h"
28
29static struct JitterBuffer *jbuf_new(uint32_t capacity);
30static void jbuf_clear(struct JitterBuffer *q);
31static void jbuf_free(struct JitterBuffer *q);
32static int jbuf_write(struct JitterBuffer *q, RTPMessage *m);
33static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success);
34OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count);
35bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
36 int32_t *old_br, int32_t *old_sr, int32_t *old_ch);
37bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels);
38
39
40
41ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data)
42{
43 ACSession *ac = calloc(sizeof(ACSession), 1);
44
45 if (!ac) {
46 LOGGER_WARNING("Allocation failed! Application might misbehave!");
47 return NULL;
48 }
49
50 if (create_recursive_mutex(ac->queue_mutex) != 0) {
51 LOGGER_WARNING("Failed to create recursive mutex!");
52 free(ac);
53 return NULL;
54 }
55
56 int status;
57 ac->decoder = opus_decoder_create(48000, 2, &status );
58
59 if ( status != OPUS_OK ) {
60 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status));
61 goto BASE_CLEANUP;
62 }
63
64 if ( !(ac->j_buf = jbuf_new(3)) ) {
65 LOGGER_WARNING("Jitter buffer creaton failed!");
66 opus_decoder_destroy(ac->decoder);
67 goto BASE_CLEANUP;
68 }
69
70 /* Initialize encoders with default values */
71 ac->encoder = create_audio_encoder(48000, 48000, 2);
72 if (ac->encoder == NULL)
73 goto DECODER_CLEANUP;
74
75 ac->test_encoder = create_audio_encoder(48000, 48000, 2);
76 if (ac->test_encoder == NULL) {
77 opus_encoder_destroy(ac->encoder);
78 goto DECODER_CLEANUP;
79 }
80
81 ac->last_encoding_bit_rate = 48000;
82 ac->last_encoding_sampling_rate = 48000;
83 ac->last_encoding_channel_count = 2;
84
85 ac->last_test_encoding_bit_rate = 48000;
86 ac->last_test_encoding_sampling_rate = 48000;
87 ac->last_test_encoding_channel_count = 2;
88
89 ac->last_decoding_channel_count = 2;
90 ac->last_decoding_sampling_rate = 48000;
91 ac->last_decoder_reconfiguration = 0; /* Make it possible to reconfigure straight away */
92
93 /* These need to be set in order to properly
94 * do error correction with opus */
95 ac->last_packet_frame_duration = 120;
96 ac->last_packet_sampling_rate = 48000;
97 ac->last_packet_channel_count = 1;
98
99 ac->av = av;
100 ac->friend_number = friend_number;
101 ac->acb.first = cb;
102 ac->acb.second = cb_data;
103
104 return ac;
105
106DECODER_CLEANUP:
107 opus_decoder_destroy(ac->decoder);
108 jbuf_free(ac->j_buf);
109BASE_CLEANUP:
110 pthread_mutex_destroy(ac->queue_mutex);
111 free(ac);
112 return NULL;
113}
114void ac_kill(ACSession* ac)
115{
116 if (!ac)
117 return;
118
119 opus_encoder_destroy(ac->encoder);
120 opus_decoder_destroy(ac->decoder);
121 jbuf_free(ac->j_buf);
122
123 pthread_mutex_destroy(ac->queue_mutex);
124
125 LOGGER_DEBUG("Terminated audio handler: %p", ac);
126 free(ac);
127}
128void ac_do(ACSession* ac)
129{
130 if (!ac)
131 return;
132
133 /* Enough space for the maximum frame size (120 ms 48 KHz audio) */
134 int16_t tmp[5760 * 2];
135
136 RTPMessage *msg;
137 int rc = 0;
138
139 pthread_mutex_lock(ac->queue_mutex);
140 while ((msg = jbuf_read(ac->j_buf, &rc)) || rc == 2) {
141 pthread_mutex_unlock(ac->queue_mutex);
142
143 if (rc == 2) {
144 LOGGER_DEBUG("OPUS correction");
145 int fs = (ac->last_packet_sampling_rate * ac->last_packet_frame_duration) / 1000;
146 rc = opus_decode(ac->decoder, NULL, 0, tmp, fs, 1);
147 } else {
148 /* Get values from packet and decode. */
149 /* NOTE: This didn't work very well
150 rc = convert_bw_to_sampling_rate(opus_packet_get_bandwidth(msg->data));
151 if (rc != -1) {
152 cs->last_packet_sampling_rate = rc;
153 } else {
154 LOGGER_WARNING("Failed to load packet values!");
155 rtp_free_msg(msg);
156 continue;
157 }*/
158
159
160 /* Pick up sampling rate from packet */
161 memcpy(&ac->last_packet_sampling_rate, msg->data, 4);
162 ac->last_packet_sampling_rate = ntohl(ac->last_packet_sampling_rate);
163
164 ac->last_packet_channel_count = opus_packet_get_nb_channels(msg->data + 4);
165
166 /** NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa,
167 * it didn't work quite well.
168 */
169 if (!reconfigure_audio_decoder(ac, ac->last_packet_sampling_rate, ac->last_packet_channel_count)) {
170 LOGGER_WARNING("Failed to reconfigure decoder!");
171 rtp_free_msg(msg);
172 continue;
173 }
174
175 rc = opus_decode(ac->decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0);
176 rtp_free_msg(msg);
177 }
178
179 if (rc < 0) {
180 LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
181 } else if (ac->acb.first) {
182 ac->last_packet_frame_duration = (rc * 1000) / ac->last_packet_sampling_rate;
183
184 ac->acb.first(ac->av, ac->friend_number, tmp, rc * ac->last_packet_channel_count,
185 ac->last_packet_channel_count, ac->last_packet_sampling_rate, ac->acb.second);
186 }
187
188 return;
189 }
190 pthread_mutex_unlock(ac->queue_mutex);
191}
192int ac_queue_message(void* acp, struct RTPMessage_s *msg)
193{
194 if (!acp || !msg)
195 return -1;
196
197 if ((msg->header->marker_payloadt & 0x7f) == (rtp_TypeAudio + 2) % 128) {
198 LOGGER_WARNING("Got dummy!");
199 rtp_free_msg(msg);
200 return 0;
201 }
202
203 if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeAudio % 128) {
204 LOGGER_WARNING("Invalid payload type!");
205 rtp_free_msg(msg);
206 return -1;
207 }
208
209 ACSession* ac = acp;
210
211 pthread_mutex_lock(ac->queue_mutex);
212 int rc = jbuf_write(ac->j_buf, msg);
213 pthread_mutex_unlock(ac->queue_mutex);
214
215 if (rc == -1) {
216 LOGGER_WARNING("Could not queue the message!");
217 rtp_free_msg(msg);
218 return -1;
219 }
220
221 return 0;
222}
223int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels)
224{
225 if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate, sampling_rate, channels,
226 &ac->last_encoding_bit_rate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count))
227 return -1;
228
229 LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels);
230 return 0;
231}
232int ac_reconfigure_test_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels)
233{
234 if (!ac || !reconfigure_audio_encoder(&ac->test_encoder, bit_rate, sampling_rate, channels,
235 &ac->last_encoding_bit_rate, &ac->last_encoding_sampling_rate, &ac->last_encoding_channel_count))
236 return -1;
237
238 LOGGER_DEBUG ("Reconfigured test audio encoder br: %d sr: %d cc:%d", bit_rate, sampling_rate, channels);
239 return 0;
240}
241
242
243
244struct JitterBuffer {
245 RTPMessage **queue;
246 uint32_t size;
247 uint32_t capacity;
248 uint16_t bottom;
249 uint16_t top;
250};
251
252static struct JitterBuffer *jbuf_new(uint32_t capacity)
253{
254 unsigned int size = 1;
255
256 while (size <= (capacity * 4)) {
257 size *= 2;
258 }
259
260 struct JitterBuffer *q;
261
262 if ( !(q = calloc(sizeof(struct JitterBuffer), 1)) ) return NULL;
263
264 if (!(q->queue = calloc(sizeof(RTPMessage *), size))) {
265 free(q);
266 return NULL;
267 }
268
269 q->size = size;
270 q->capacity = capacity;
271 return q;
272}
273static void jbuf_clear(struct JitterBuffer *q)
274{
275 for (; q->bottom != q->top; ++q->bottom) {
276 if (q->queue[q->bottom % q->size]) {
277 rtp_free_msg(q->queue[q->bottom % q->size]);
278 q->queue[q->bottom % q->size] = NULL;
279 }
280 }
281}
282static void jbuf_free(struct JitterBuffer *q)
283{
284 if (!q) return;
285
286 jbuf_clear(q);
287 free(q->queue);
288 free(q);
289}
290static int jbuf_write(struct JitterBuffer *q, RTPMessage *m)
291{
292 uint16_t sequnum = m->header->sequnum;
293
294 unsigned int num = sequnum % q->size;
295
296 if ((uint32_t)(sequnum - q->bottom) > q->size) {
297 LOGGER_DEBUG("Clearing filled jitter buffer: %p", q);
298
299 jbuf_clear(q);
300 q->bottom = sequnum - q->capacity;
301 q->queue[num] = m;
302 q->top = sequnum + 1;
303 return 0;
304 }
305
306 if (q->queue[num])
307 return -1;
308
309 q->queue[num] = m;
310
311 if ((sequnum - q->bottom) >= (q->top - q->bottom))
312 q->top = sequnum + 1;
313
314 return 0;
315}
316static RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
317{
318 if (q->top == q->bottom) {
319 *success = 0;
320 return NULL;
321 }
322
323 unsigned int num = q->bottom % q->size;
324
325 if (q->queue[num]) {
326 RTPMessage *ret = q->queue[num];
327 q->queue[num] = NULL;
328 ++q->bottom;
329 *success = 1;
330 return ret;
331 }
332
333 if ((uint32_t)(q->top - q->bottom) > q->capacity) {
334 ++q->bottom;
335 *success = 2;
336 return NULL;
337 }
338
339 *success = 0;
340 return NULL;
341}
342OpusEncoder* create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count)
343{
344 int status = OPUS_OK;
345 OpusEncoder* rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_AUDIO, &status);
346
347 if ( status != OPUS_OK ) {
348 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status));
349 return NULL;
350 }
351
352 status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bit_rate));
353
354 if ( status != OPUS_OK ) {
355 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
356 goto FAILURE;
357 }
358
359 status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10));
360
361 if ( status != OPUS_OK ) {
362 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
363 goto FAILURE;
364 }
365
366 return rc;
367
368FAILURE:
369 opus_encoder_destroy(rc);
370 return NULL;
371}
372bool reconfigure_audio_encoder(OpusEncoder** e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
373 int32_t* old_br, int32_t* old_sr, int32_t* old_ch)
374{
375 /* Values are checked in toxav.c */
376 if (*old_sr != new_sr || *old_ch != new_ch) {
377 OpusEncoder* new_encoder = create_audio_encoder(new_br, new_sr, new_ch);
378 if (new_encoder == NULL)
379 return false;
380
381 opus_encoder_destroy(*e);
382 *e = new_encoder;
383 } else if (*old_br == new_br)
384 return true; /* Nothing changed */
385 else {
386 int status = opus_encoder_ctl(*e, OPUS_SET_BITRATE(new_br));
387
388 if ( status != OPUS_OK ) {
389 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
390 return false;
391 }
392 }
393
394 *old_br = new_br;
395 *old_sr = new_sr;
396 *old_ch = new_ch;
397
398 return true;
399}
400bool reconfigure_audio_decoder(ACSession* ac, int32_t sampling_rate, int8_t channels)
401{
402 if (sampling_rate != ac->last_decoding_sampling_rate || channels != ac->last_decoding_channel_count) {
403 if (current_time_monotonic() - ac->last_decoder_reconfiguration < 500)
404 return false;
405
406 int status;
407 OpusDecoder* new_dec = opus_decoder_create(sampling_rate, channels, &status );
408 if ( status != OPUS_OK ) {
409 LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status));
410 return false;
411 }
412
413 ac->last_decoding_sampling_rate = sampling_rate;
414 ac->last_decoding_channel_count = channels;
415 ac->last_decoder_reconfiguration = current_time_monotonic();
416
417 opus_decoder_destroy(ac->decoder);
418 ac->decoder = new_dec;
419
420 LOGGER_DEBUG("Reconfigured audio decoder sr: %d cc: %d", sampling_rate, channels);
421 }
422
423 return true;
424} \ No newline at end of file
diff --git a/toxav/audio.h b/toxav/audio.h
new file mode 100644
index 00000000..9ef10ae4
--- /dev/null
+++ b/toxav/audio.h
@@ -0,0 +1,89 @@
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_s;
33
34/*
35 * Base Audio Codec session type.
36 */
37typedef struct ACSession_s {
38 /* encoding */
39 OpusEncoder *encoder;
40 int32_t last_encoding_sampling_rate;
41 int32_t last_encoding_channel_count;
42 int32_t last_encoding_bit_rate;
43
44 /* Testing encoder for dynamic bit rate streaming */
45 OpusEncoder *test_encoder;
46 int32_t last_test_encoding_sampling_rate;
47 int32_t last_test_encoding_channel_count;
48 int32_t last_test_encoding_bit_rate;
49
50 /* decoding */
51 OpusDecoder *decoder;
52 int32_t last_packet_channel_count;
53 int32_t last_packet_sampling_rate;
54 int32_t last_packet_frame_duration;
55 int32_t last_decoding_sampling_rate;
56 int32_t last_decoding_channel_count;
57 uint64_t last_decoder_reconfiguration;
58 void *j_buf;
59
60 pthread_mutex_t queue_mutex[1];
61
62 ToxAV* av;
63 uint32_t friend_number;
64 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
65} ACSession;
66
67/*
68 * Create new Audio Codec session.
69 */
70ACSession* ac_new(ToxAV* av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data);
71/*
72 * Kill the Audio Codec session.
73 */
74void ac_kill(ACSession* ac);
75/*
76 * Do periodic work. Work is consisted out of decoding only.
77 */
78void ac_do(ACSession* ac);
79/*
80 * Queue new rtp message.
81 */
82int ac_queue_message(void *acp, struct RTPMessage_s *msg);
83/*
84 * Set new values to the encoders.
85 */
86int ac_reconfigure_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels);
87int ac_reconfigure_test_encoder(ACSession* ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels);
88
89#endif /* AUDIO_H */ \ No newline at end of file
diff --git a/toxav/codec.c b/toxav/codec.c
deleted file mode 100644
index de802526..00000000
--- a/toxav/codec.c
+++ /dev/null
@@ -1,688 +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 "../toxcore/logger.h"
30#include "../toxcore/util.h"
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <math.h>
35#include <assert.h>
36#include <time.h>
37
38#include "msi.h"
39#include "rtp.h"
40#include "codec.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_AUDIO, &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 rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
312
313 if ( rc != OPUS_OK ) {
314 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
315 return -1;
316 }
317
318 return 0;
319}
320
321/* PUBLIC */
322int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length)
323{
324 if (!cs || !length || length > cs->max_video_frame_size) {
325 LOGGER_ERROR("Invalid CodecState or video frame size: %u", length);
326 return cs_ErrorSplittingVideoPayload;
327 }
328
329 cs->split_video_frame[0] = cs->frameid_out++;
330 cs->split_video_frame[1] = 0;
331 cs->processing_video_frame = payload;
332 cs->processing_video_frame_size = length;
333
334 return ((length - 1) / cs->video_frame_piece_size) + 1;
335}
336
337const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size)
338{
339 if (!cs || !size) return NULL;
340
341 if (cs->processing_video_frame_size > cs->video_frame_piece_size) {
342 memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE,
343 cs->processing_video_frame,
344 cs->video_frame_piece_size);
345
346 cs->processing_video_frame += cs->video_frame_piece_size;
347 cs->processing_video_frame_size -= cs->video_frame_piece_size;
348
349 *size = cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE;
350 } else {
351 memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE,
352 cs->processing_video_frame,
353 cs->processing_video_frame_size);
354
355 *size = cs->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE;
356 }
357
358 cs->split_video_frame[1]++;
359
360 return cs->split_video_frame;
361}
362
363void cs_do(CSSession *cs)
364{
365 /* Codec session should always be protected by call mutex so no need to check for cs validity
366 */
367
368 if (!cs) return;
369
370 Payload *p;
371 int rc;
372
373 int success = 0;
374
375 pthread_mutex_lock(cs->queue_mutex);
376 RTPMessage *msg;
377
378 while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) {
379 pthread_mutex_unlock(cs->queue_mutex);
380
381 uint16_t fsize = ((cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000);
382 int16_t tmp[fsize * cs->audio_decoder_channels];
383
384 if (success == 2) {
385 rc = opus_decode(cs->audio_decoder, 0, 0, tmp, fsize, 1);
386 } else {
387 rc = opus_decode(cs->audio_decoder, msg->data, msg->length, tmp, fsize, 0);
388 rtp_free_msg(NULL, msg);
389 }
390
391 if (rc < 0) {
392 LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
393 } else if (cs->acb.first) {
394 /* Play */
395 cs->acb.first(cs->agent, cs->call_idx, tmp, rc, cs->acb.second);
396 }
397
398 pthread_mutex_lock(cs->queue_mutex);
399 }
400
401 if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) {
402 /* Decode video */
403 buffer_read(cs->vbuf_raw, &p);
404
405 /* Leave space for (possibly) other thread to queue more data after we read it here */
406 pthread_mutex_unlock(cs->queue_mutex);
407
408 rc = vpx_codec_decode(&cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US);
409 free(p);
410
411 if (rc != VPX_CODEC_OK) {
412 LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc));
413 } else {
414 vpx_codec_iter_t iter = NULL;
415 vpx_image_t *dest = vpx_codec_get_frame(&cs->v_decoder, &iter);
416
417 /* Play decoded images */
418 for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) {
419 if (cs->vcb.first)
420 cs->vcb.first(cs->agent, cs->call_idx, dest, cs->vcb.second);
421
422 vpx_img_free(dest);
423 }
424 }
425
426 return;
427 }
428
429 pthread_mutex_unlock(cs->queue_mutex);
430}
431
432int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height)
433{
434 vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
435
436 if (cfg.g_w == width && cfg.g_h == height)
437 return 0;
438
439 if (width * height > cs->max_width * cs->max_height) {
440 vpx_codec_ctx_t v_encoder = cs->v_encoder;
441
442 if (init_video_encoder(cs, width, height, cs->video_bitrate) == -1) {
443 cs->v_encoder = v_encoder;
444 return cs_ErrorSettingVideoResolution;
445 }
446
447 vpx_codec_destroy(&v_encoder);
448 return 0;
449 }
450
451 LOGGER_DEBUG("New video resolution: %u %u", width, height);
452 cfg.g_w = width;
453 cfg.g_h = height;
454 int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg);
455
456 if ( rc != VPX_CODEC_OK) {
457 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
458 return cs_ErrorSettingVideoResolution;
459 }
460
461 return 0;
462}
463
464int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate)
465{
466 vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
467
468 if (cfg.rc_target_bitrate == video_bitrate)
469 return 0;
470
471 LOGGER_DEBUG("New video bitrate: %u", video_bitrate);
472 cfg.rc_target_bitrate = video_bitrate;
473 int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg);
474
475 if ( rc != VPX_CODEC_OK) {
476 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
477 return cs_ErrorSettingVideoBitrate;
478 }
479
480 cs->video_bitrate = video_bitrate;
481 return 0;
482}
483
484CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video)
485{
486 CSSession *cs = calloc(sizeof(CSSession), 1);
487
488 if (!cs) {
489 LOGGER_WARNING("Allocation failed! Application might misbehave!");
490 return NULL;
491 }
492
493 if (create_recursive_mutex(cs->queue_mutex) != 0) {
494 LOGGER_WARNING("Failed to create recursive mutex!");
495 free(cs);
496 return NULL;
497 }
498
499 if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) {
500 LOGGER_WARNING("Jitter buffer creaton failed!");
501 goto error;
502 }
503
504 cs->audio_encoder_bitrate = cs_self->audio_bitrate;
505 cs->audio_encoder_sample_rate = cs_self->audio_sample_rate;
506 cs->audio_encoder_channels = cs_self->audio_channels;
507 cs->audio_encoder_frame_duration = cs_self->audio_frame_duration;
508
509 cs->audio_decoder_bitrate = cs_peer->audio_bitrate;
510 cs->audio_decoder_sample_rate = cs_peer->audio_sample_rate;
511 cs->audio_decoder_channels = cs_peer->audio_channels;
512 cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration;
513
514
515 cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? cs_AudioEncoding : 0;
516 cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? cs_AudioDecoding : 0;
517
518 if ( !(cs->capabilities & cs_AudioEncoding) || !(cs->capabilities & cs_AudioDecoding) ) goto error;
519
520 if ((cs->support_video = has_video)) {
521 cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE;
522 cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE;
523
524 cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width,
525 cs_self->max_video_height, cs_self->video_bitrate) ) ? cs_VideoEncoding : 0;
526 cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? cs_VideoDecoding : 0;
527
528 if ( !(cs->capabilities & cs_VideoEncoding) || !(cs->capabilities & cs_VideoDecoding) ) goto error;
529
530 if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) goto error;
531
532 if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) )
533 goto error;
534
535 if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error;
536 }
537
538 return cs;
539
540error:
541 LOGGER_WARNING("Error initializing codec session! Application might misbehave!");
542
543 pthread_mutex_destroy(cs->queue_mutex);
544
545 if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder);
546
547 if ( cs->audio_decoder ) opus_decoder_destroy(cs->audio_decoder);
548
549
550 if (has_video) {
551 if ( cs->capabilities & cs_VideoDecoding ) vpx_codec_destroy(&cs->v_decoder);
552
553 if ( cs->capabilities & cs_VideoEncoding ) vpx_codec_destroy(&cs->v_encoder);
554
555 buffer_free(cs->vbuf_raw);
556
557 free(cs->frame_buf);
558 free(cs->split_video_frame);
559 }
560
561 jbuf_free(cs->j_buf);
562 free(cs);
563
564 return NULL;
565}
566
567void cs_kill(CSSession *cs)
568{
569 if (!cs) return;
570
571 /* queue_message will not be called since it's unregistered before cs_kill is called */
572 pthread_mutex_destroy(cs->queue_mutex);
573
574
575 if ( cs->audio_encoder )
576 opus_encoder_destroy(cs->audio_encoder);
577
578 if ( cs->audio_decoder )
579 opus_decoder_destroy(cs->audio_decoder);
580
581 if ( cs->capabilities & cs_VideoDecoding )
582 vpx_codec_destroy(&cs->v_decoder);
583
584 if ( cs->capabilities & cs_VideoEncoding )
585 vpx_codec_destroy(&cs->v_encoder);
586
587 jbuf_free(cs->j_buf);
588 buffer_free(cs->vbuf_raw);
589 free(cs->frame_buf);
590 free(cs->split_video_frame);
591
592 LOGGER_DEBUG("Terminated codec state: %p", cs);
593 free(cs);
594}
595
596
597
598
599/* Called from RTP */
600void queue_message(RTPSession *session, RTPMessage *msg)
601{
602 /* This function is unregistered during call termination befor destroing
603 * Codec session so no need to check for validity of cs
604 */
605 CSSession *cs = session->cs;
606
607 if (!cs) return;
608
609 /* Audio */
610 if (session->payload_type == msi_TypeAudio % 128) {
611 pthread_mutex_lock(cs->queue_mutex);
612 int ret = jbuf_write(cs->j_buf, msg);
613 pthread_mutex_unlock(cs->queue_mutex);
614
615 if (ret == -1) {
616 rtp_free_msg(NULL, msg);
617 }
618 }
619 /* Video */
620 else {
621 uint8_t *packet = msg->data;
622 uint32_t packet_size = msg->length;
623
624 if (packet_size < VIDEOFRAME_HEADER_SIZE)
625 goto end;
626
627 uint8_t diff = packet[0] - cs->frameid_in;
628
629 if (diff != 0) {
630 if (diff < 225) { /* New frame */
631 /* Flush last frames' data and get ready for this frame */
632 Payload *p = malloc(sizeof(Payload) + cs->frame_size);
633
634 if (p) {
635 pthread_mutex_lock(cs->queue_mutex);
636
637 if (buffer_full(cs->vbuf_raw)) {
638 LOGGER_DEBUG("Dropped video frame");
639 Payload *tp;
640 buffer_read(cs->vbuf_raw, &tp);
641 free(tp);
642 } else {
643 p->size = cs->frame_size;
644 memcpy(p->data, cs->frame_buf, cs->frame_size);
645 }
646
647 buffer_write(cs->vbuf_raw, p);
648 pthread_mutex_unlock(cs->queue_mutex);
649 } else {
650 LOGGER_WARNING("Allocation failed! Program might misbehave!");
651 goto end;
652 }
653
654 cs->last_timestamp = msg->header->timestamp;
655 cs->frameid_in = packet[0];
656 memset(cs->frame_buf, 0, cs->frame_size);
657 cs->frame_size = 0;
658
659 } else { /* Old frame; drop */
660 LOGGER_DEBUG("Old packet: %u", packet[0]);
661 goto end;
662 }
663 }
664
665 uint8_t piece_number = packet[1];
666
667 uint32_t length_before_piece = ((piece_number - 1) * cs->video_frame_piece_size);
668 uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE);
669
670 if (framebuf_new_length > cs->max_video_frame_size) {
671 goto end;
672 }
673
674 /* Otherwise it's part of the frame so just process */
675 /* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */
676
677 memcpy(cs->frame_buf + length_before_piece,
678 packet + VIDEOFRAME_HEADER_SIZE,
679 packet_size - VIDEOFRAME_HEADER_SIZE);
680
681 if (framebuf_new_length > cs->frame_size) {
682 cs->frame_size = framebuf_new_length;
683 }
684
685end:
686 rtp_free_msg(NULL, msg);
687 }
688}
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/msi.c b/toxav/msi.c
index dcb7b62a..d3559160 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 *
@@ -33,163 +33,336 @@
33#include <string.h> 33#include <string.h>
34#include <stdlib.h> 34#include <stdlib.h>
35#include <stdbool.h> 35#include <stdbool.h>
36#include <assert.h>
36 37
37#define MSI_MAXMSG_SIZE 256 38#define MSI_MAXMSG_SIZE 256
38 39
39/* Define default timeout for a request.
40 * There is no behavior specified by the msi on what will
41 * client do on timeout, but to call timeout callback.
42 */
43#define m_deftout 10000 /* in milliseconds */
44
45/** 40/**
46 * Protocol: 41 * Protocol:
47 * 42 *
48 * |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}| 43 * |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}|
49 */ 44 */
50 45
51typedef uint8_t MSIRawCSettingsType[23];
52
53typedef enum { 46typedef enum {
54 IDRequest = 1, 47 IDRequest = 1,
55 IDResponse, 48 IDError,
56 IDReason, 49 IDCapabilities,
57 IDCallId, 50 IDVFPSZ,
58 IDCSettings,
59 51
60} MSIHeaderID; 52} MSIHeaderID;
61 53
62typedef enum {
63 TypeRequest,
64 TypeResponse,
65
66} MSIMessageType;
67 54
68typedef enum { 55typedef enum {
69 invite, 56 requ_push,
70 start, 57 requ_pop,
71 cancel,
72 reject,
73 end,
74
75} MSIRequest; 58} MSIRequest;
76 59
77typedef enum {
78 ringing,
79 starting,
80 ending,
81 error
82
83} MSIResponse;
84
85 60
86#define GENERIC_HEADER(header, val_type) \ 61#define GENERIC_HEADER(header, val_type) \
87typedef struct _MSIHeader##header { \ 62typedef struct { \
88val_type value; \ 63 val_type value; \
89_Bool exists; \ 64 bool exists; \
90} MSIHeader##header; 65} MSIHeader##header
91
92
93GENERIC_HEADER ( Request, MSIRequest )
94GENERIC_HEADER ( Response, MSIResponse )
95GENERIC_HEADER ( CallId, MSICallIDType )
96GENERIC_HEADER ( Reason, MSIReasonStrType )
97GENERIC_HEADER ( CSettings, MSIRawCSettingsType )
98
99 66
100typedef struct _MSIMessage {
101 67
102 MSIHeaderRequest request; 68GENERIC_HEADER ( Request, MSIRequest );
103 MSIHeaderResponse response; 69GENERIC_HEADER ( Error, MSIError );
104 MSIHeaderReason reason; 70GENERIC_HEADER ( Capabilities, uint8_t );
105 MSIHeaderCallId callid; 71GENERIC_HEADER ( VFPSZ, uint16_t );
106 MSIHeaderCSettings csettings;
107 72
108 int friend_id;
109 73
74typedef struct {
75 MSIHeaderRequest request;
76 MSIHeaderError error;
77 MSIHeaderCapabilities capabilities;
78 MSIHeaderVFPSZ vfpsz; /* Video frame piece size. NOTE: Value must be in network b-order TODO: get rid of this eventually */
110} MSIMessage; 79} MSIMessage;
111 80
112 81
113static void invoke_callback(MSISession *s, int32_t c, MSICallbackID i) 82void msg_init (MSIMessage *dest, MSIRequest request);
114{ 83int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length );
115 if ( s->callbacks[i].first ) { 84uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length );
116 LOGGER_DEBUG("Invoking callback function: %d", i); 85int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg );
86int send_error ( Messenger* m, uint32_t friend_number, MSIError error );
87static int invoke_callback(MSICall* call, MSICallbackID cb);
88static MSICall *get_call ( MSISession *session, uint32_t friend_number );
89MSICall *new_call ( MSISession *session, uint32_t friend_number );
90void kill_call ( MSICall *call );
91void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data);
92void handle_push ( MSICall *call, const MSIMessage *msg );
93void handle_pop ( MSICall *call, const MSIMessage *msg );
94void handle_msi_packet ( Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object );
117 95
118 s->callbacks[i].first( s->agent_handler, c, s->callbacks[i].second );
119 }
120}
121 96
122/** 97/**
123 * Parse raw 'data' received from socket into MSIMessage struct. 98 * Public functions
124 * Every message has to have end value of 'end_byte' or _undefined_ behavior
125 * occures. The best practice is to check the end of the message at the handle_packet.
126 */ 99 */
127static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length ) 100void msi_register_callback ( MSISession* session, msi_action_cb* callback, MSICallbackID id)
128{ 101{
129 102 pthread_mutex_lock(session->mutex);
130#define FAIL_CONSTRAINT(constraint, wanted) if ((constraint -= wanted) < 1) { LOGGER_ERROR("Read over length!"); return -1; } 103 session->callbacks[id] = callback;
131#define FAIL_SIZE(byte, valid) if ( byte != valid ) { LOGGER_ERROR("Invalid data size!"); return -1; } 104 pthread_mutex_unlock(session->mutex);
132#define FAIL_LIMITS(byte, high) if ( byte > high ) { LOGGER_ERROR("Failed limit!"); return -1; } 105}
133 106MSISession *msi_new ( Messenger *m )
134 if ( msg == NULL ) { 107{
135 LOGGER_ERROR("Could not parse message: no storage!"); 108 if (m == NULL) {
109 LOGGER_ERROR("Could not init session on empty messenger!");
110 return NULL;
111 }
112
113 MSISession *retu = calloc ( sizeof ( MSISession ), 1 );
114
115 if (retu == NULL) {
116 LOGGER_ERROR("Allocation failed! Program might misbehave!");
117 return NULL;
118 }
119
120 if (create_recursive_mutex(retu->mutex) != 0) {
121 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
122 free(retu);
123 return NULL;
124 }
125
126 retu->messenger = m;
127
128 m_callback_msi_packet(m, handle_msi_packet, retu );
129
130 /* This is called when remote terminates session */
131 m_callback_connectionstatus_internal_av(m, on_peer_status, retu);
132
133 LOGGER_DEBUG("New msi session: %p ", retu);
134 return retu;
135}
136int msi_kill ( MSISession *session )
137{
138 if (session == NULL) {
139 LOGGER_ERROR("Tried to terminate non-existing session");
140 return -1;
141 }
142
143 m_callback_msi_packet((struct Messenger *) session->messenger, NULL, NULL);
144 pthread_mutex_lock(session->mutex);
145
146 if (session->calls) {
147 MSIMessage msg;
148 msg_init(&msg, requ_pop);
149
150 MSICall* it = get_call(session, session->calls_head);
151 for (; it; it = it->next) {
152 send_message(session->messenger, it->friend_number, &msg);
153 kill_call(it); /* This will eventually free session->calls */
154 }
155 }
156
157 pthread_mutex_unlock(session->mutex);
158 pthread_mutex_destroy(session->mutex);
159
160 LOGGER_DEBUG("Terminated session: %p", session);
161 free ( session );
162 return 0;
163}
164int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities )
165{
166 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number);
167
168 pthread_mutex_lock(session->mutex);
169 if (get_call(session, friend_number) != NULL) {
170 LOGGER_ERROR("Already in a call");
171 pthread_mutex_unlock(session->mutex);
172 return -1;
173 }
174
175 (*call) = new_call ( session, friend_number );
176
177 if ( *call == NULL ) {
178 pthread_mutex_unlock(session->mutex);
179 return -1;
180 }
181
182 (*call)->self_capabilities = capabilities;
183
184 MSIMessage msg;
185 msg_init(&msg, requ_push);
186
187 msg.capabilities.exists = true;
188 msg.capabilities.value = capabilities;
189
190 msg.vfpsz.exists = true;
191 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE;
192
193 send_message ( (*call)->session->messenger, (*call)->friend_number, &msg );
194
195 (*call)->state = msi_CallRequesting;
196
197 LOGGER_DEBUG("Invite sent");
198 pthread_mutex_unlock(session->mutex);
199 return 0;
200}
201int msi_hangup ( MSICall* call )
202{
203 LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_number);
204
205 MSISession* session = call->session;
206 pthread_mutex_lock(session->mutex);
207
208 if ( call->state == msi_CallInactive ) {
209 LOGGER_ERROR("Call is in invalid state!");
210 pthread_mutex_unlock(session->mutex);
136 return -1; 211 return -1;
137 } 212 }
213
214 MSIMessage msg;
215 msg_init(&msg, requ_pop);
216
217 send_message ( session->messenger, call->friend_number, &msg );
218
219 kill_call(call);
220 pthread_mutex_unlock(session->mutex);
221 return 0;
222}
223int msi_answer ( MSICall* call, uint8_t capabilities )
224{
225 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_number);
226
227 MSISession* session = call->session;
228 pthread_mutex_lock(session->mutex);
229
230 if ( call->state != msi_CallRequested ) {
231 /* Though sending in invalid state will not cause anything wierd
232 * Its better to not do it like a maniac */
233 LOGGER_ERROR("Call is in invalid state!");
234 pthread_mutex_unlock(session->mutex);
235 return -1;
236 }
237
238 call->self_capabilities = capabilities;
239
240 MSIMessage msg;
241 msg_init(&msg, requ_push);
242
243 msg.capabilities.exists = true;
244 msg.capabilities.value = capabilities;
245
246 msg.vfpsz.exists = true;
247 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE;
248
249 send_message ( session->messenger, call->friend_number, &msg );
250
251 call->state = msi_CallActive;
252 pthread_mutex_unlock(session->mutex);
253
254 return 0;
255}
256int msi_change_capabilities( MSICall* call, uint8_t capabilities )
257{
258 LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number);
259
260 MSISession* session = call->session;
261 pthread_mutex_lock(session->mutex);
262
263 if ( call->state != msi_CallActive ) {
264 /* Sending capabilities change can cause error on other side if
265 * the call is not active since we don't send header 'vfpsz'.
266 * If we were to send 'vfpsz' while call is active it would be
267 * ignored. However, if call is not active peer will expect
268 * the said header on 'push' so that it could handle the call
269 * like new. TODO: explain this better
270 */
271 LOGGER_ERROR("Call is in invalid state!");
272 pthread_mutex_unlock(session->mutex);
273 return -1;
274 }
275
276 call->self_capabilities = capabilities;
277
278 MSIMessage msg;
279 msg_init(&msg, requ_push);
280
281 msg.capabilities.exists = true;
282 msg.capabilities.value = capabilities;
283
284 send_message ( call->session->messenger, call->friend_number, &msg );
285
286 pthread_mutex_unlock(session->mutex);
287 return 0;
288}
289
138 290
139 if ( data[length - 1] ) { /* End byte must have value 0 */ 291/**
292 * Private functions
293 */
294void msg_init(MSIMessage* dest, MSIRequest request)
295{
296 memset(dest, 0, sizeof(*dest));
297 dest->request.exists = true;
298 dest->request.value = request;
299}
300int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
301{
302 /* Parse raw data received from socket into MSIMessage struct */
303
304#define CHECK_SIZE(bytes, constraint, size) \
305 if ((constraint -= (2 + size)) < 1) { LOGGER_ERROR("Read over length!"); return -1; } \
306 if ( bytes[1] != size ) { LOGGER_ERROR("Invalid data size!"); return -1; }
307
308#define CHECK_ENUM_HIGH(bytes, enum_high) /* Assumes size == 1 */ \
309 if ( bytes[2] > enum_high ) { LOGGER_ERROR("Failed enum high limit!"); return -1; }
310
311#define SET_UINT8(bytes, header) do { \
312 header.value = bytes[2]; \
313 header.exists = true; \
314 bytes += 3; \
315 } while(0)
316
317#define SET_UINT16(bytes, header) do { \
318 memcpy(&header.value, bytes + 2, 2);\
319 header.exists = true; \
320 bytes += 4; \
321 } while(0)
322
323
324 assert(dest);
325
326 if ( length == 0 || data[length - 1] ) { /* End byte must have value 0 */
140 LOGGER_ERROR("Invalid end byte"); 327 LOGGER_ERROR("Invalid end byte");
141 return -1; 328 return -1;
142 } 329 }
143 330
331 memset(dest, 0, sizeof(*dest));
332
144 const uint8_t *it = data; 333 const uint8_t *it = data;
145 int size_constraint = length; 334 int size_constraint = length;
146 335
147 while ( *it ) {/* until end byte is hit */ 336 while ( *it ) {/* until end byte is hit */
148 switch (*it) { 337 switch (*it) {
149 case IDRequest: 338 case IDRequest:
150 FAIL_CONSTRAINT(size_constraint, 3); 339 CHECK_SIZE(it, size_constraint, 1);
151 FAIL_SIZE(it[1], 1); 340 CHECK_ENUM_HIGH(it, requ_pop);
152// FAIL_LIMITS(it[2], invite, end); 341 SET_UINT8(it, dest->request);
153 FAIL_LIMITS(it[2], end);
154 msg->request.value = it[2];
155 it += 3;
156 msg->request.exists = 1;
157 break; 342 break;
158 343
159 case IDResponse: 344 case IDError:
160 FAIL_CONSTRAINT(size_constraint, 3); 345 CHECK_SIZE(it, size_constraint, 1);
161 FAIL_SIZE(it[1], 1); 346 CHECK_ENUM_HIGH(it, msi_EUndisclosed);
162// FAIL_LIMITS(it[2], ringing, error); 347 SET_UINT8(it, dest->error);
163 FAIL_LIMITS(it[2], error);
164 msg->response.value = it[2];
165 it += 3;
166 msg->response.exists = 1;
167 break;
168
169 case IDCallId:
170 FAIL_CONSTRAINT(size_constraint, sizeof(MSICallIDType) + 2);
171 FAIL_SIZE(it[1], sizeof(MSICallIDType));
172 memcpy(msg->callid.value, it + 2, sizeof(MSICallIDType));
173 it += sizeof(MSICallIDType) + 2;
174 msg->callid.exists = 1;
175 break; 348 break;
176 349
177 case IDReason: 350 case IDCapabilities:
178 FAIL_CONSTRAINT(size_constraint, sizeof(MSIReasonStrType) + 2); 351 CHECK_SIZE(it, size_constraint, 1);
179 FAIL_SIZE(it[1], sizeof(MSIReasonStrType)); 352 SET_UINT8(it, dest->capabilities);
180 memcpy(msg->reason.value, it + 2, sizeof(MSIReasonStrType));
181 it += sizeof(MSIReasonStrType) + 2;
182 msg->reason.exists = 1;
183 break; 353 break;
184 354
185 case IDCSettings: 355 case IDVFPSZ:
186 FAIL_CONSTRAINT(size_constraint, sizeof(MSIRawCSettingsType) + 2); 356 CHECK_SIZE(it, size_constraint, 2);
187 FAIL_SIZE(it[1], sizeof(MSIRawCSettingsType)); 357 SET_UINT16(it, dest->vfpsz);
188 memcpy(msg->csettings.value, it + 2, sizeof(MSIRawCSettingsType)); 358 dest->vfpsz.value = ntohs(dest->vfpsz.value);
189 it += sizeof(MSIRawCSettingsType) + 2; 359
190 msg->csettings.exists = 1; 360 if (dest->vfpsz.value > 1200) {
361 LOGGER_ERROR("Invalid vfpsz param");
362 return -1;
363 }
191 break; 364 break;
192 365
193 default: 366 default:
194 LOGGER_ERROR("Invalid id byte"); 367 LOGGER_ERROR("Invalid id byte");
195 return -1; 368 return -1;
@@ -197,80 +370,25 @@ static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t lengt
197 } 370 }
198 } 371 }
199 372
200 return 0; 373 if (dest->request.exists == false) {
201} 374 LOGGER_ERROR("Invalid request field!");
202 375 return -1;
203/**
204 * Create the message.
205 */
206MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value )
207{
208 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 );
209
210 if ( retu == NULL ) {
211 LOGGER_WARNING("Allocation failed! Program might misbehave!");
212 return NULL;
213 }
214
215 if ( type == TypeRequest ) {
216 retu->request.exists = 1;
217 retu->request.value = type_value;
218
219 } else {
220 retu->response.exists = 1;
221 retu->response.value = type_value;
222 }
223
224 return retu;
225}
226
227
228/**
229 * Parse data from handle_packet.
230 */
231MSIMessage *parse_recv ( const uint8_t *data, uint16_t length )
232{
233 if ( data == NULL ) {
234 LOGGER_WARNING("Tried to parse empty message!");
235 return NULL;
236 }
237
238 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 );
239
240 if ( retu == NULL ) {
241 LOGGER_WARNING("Allocation failed! Program might misbehave!");
242 return NULL;
243 }
244
245 if ( parse_raw_data ( retu, data, length ) == -1 ) {
246
247 free ( retu );
248 return NULL;
249 } 376 }
377
378 return 0;
250 379
251 return retu; 380#undef CHECK_SIZE
381#undef CHECK_ENUM_HIGH
382#undef SET_UINT8
383#undef SET_UINT16
252} 384}
253 385uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length )
254
255/**
256 * Speaks for itself.
257 */
258uint8_t *format_output ( uint8_t *dest,
259 MSIHeaderID id,
260 const void *value,
261 uint8_t value_len,
262 uint16_t *length )
263{ 386{
264 if ( dest == NULL ) { 387 /* Parse a single header for sending */
265 LOGGER_ERROR("No destination space!"); 388 assert(dest);
266 return NULL; 389 assert(value);
267 } 390 assert(value_len);
268 391
269 if (value == NULL || value_len == 0) {
270 LOGGER_ERROR("Empty header value");
271 return NULL;
272 }
273
274 *dest = id; 392 *dest = id;
275 dest ++; 393 dest ++;
276 *dest = value_len; 394 *dest = value_len;
@@ -282,521 +400,211 @@ uint8_t *format_output ( uint8_t *dest,
282 400
283 return dest + value_len; /* Set to next position ready to be written */ 401 return dest + value_len; /* Set to next position ready to be written */
284} 402}
285 403int send_message ( Messenger* m, uint32_t friend_number, const MSIMessage *msg )
286
287/**
288 * Parse MSIMessage to send.
289 */
290uint16_t parse_send ( MSIMessage *msg, uint8_t *dest )
291{ 404{
292 if (msg == NULL) { 405 /* Parse and send message */
293 LOGGER_ERROR("No message!"); 406 assert(m);
294 return 0; 407
295 } 408 uint8_t parsed [MSI_MAXMSG_SIZE];
296
297 if (dest == NULL ) {
298 LOGGER_ERROR("No destination!");
299 return 0;
300 }
301 409
302 uint8_t *it = dest; 410 uint8_t *it = parsed;
303 uint16_t size = 0; 411 uint16_t size = 0;
304 412
305 if (msg->request.exists) { 413 if (msg->request.exists) {
306 uint8_t cast = msg->request.value; 414 uint8_t cast = msg->request.value;
307 it = format_output(it, IDRequest, &cast, 1, &size); 415 it = msg_parse_header_out(IDRequest, it, &cast,
308 } 416 sizeof(cast), &size);
309 417 } else {
310 if (msg->response.exists) { 418 LOGGER_DEBUG("Must have request field");
311 uint8_t cast = msg->response.value;
312 it = format_output(it, IDResponse, &cast, 1, &size);
313 }
314
315 if (msg->callid.exists) {
316 it = format_output(it, IDCallId, &msg->callid.value, sizeof(msg->callid.value), &size);
317 }
318
319 if (msg->reason.exists) {
320 it = format_output(it, IDReason, &msg->reason.value, sizeof(msg->reason.value), &size);
321 }
322
323 if (msg->csettings.exists) {
324 it = format_output(it, IDCSettings, &msg->csettings.value, sizeof(msg->csettings.value), &size);
325 }
326
327 *it = 0;
328 size ++;
329
330 return size;
331}
332
333void msi_msg_set_reason ( MSIMessage *msg, const MSIReasonStrType value )
334{
335 if ( !msg ) return;
336
337 msg->reason.exists = 1;
338 memcpy(msg->reason.value, value, sizeof(MSIReasonStrType));
339}
340
341void msi_msg_set_callid ( MSIMessage *msg, const MSICallIDType value )
342{
343 if ( !msg ) return;
344
345 msg->callid.exists = 1;
346 memcpy(msg->callid.value, value, sizeof(MSICallIDType));
347}
348
349void msi_msg_set_csettings ( MSIMessage *msg, const MSICSettings *value )
350{
351 if ( !msg ) return;
352
353 msg->csettings.exists = 1;
354
355 msg->csettings.value[0] = value->call_type;
356 uint8_t *iter = msg->csettings.value + 1;
357
358 /* Video bitrate */
359 uint32_t lval = htonl(value->video_bitrate);
360 memcpy(iter, &lval, 4);
361 iter += 4;
362
363 /* Video max width */
364 uint16_t sval = htons(value->max_video_width);
365 memcpy(iter, &sval, 2);
366 iter += 2;
367
368 /* Video max height */
369 sval = htons(value->max_video_height);
370 memcpy(iter, &sval, 2);
371 iter += 2;
372
373 /* Audio bitrate */
374 lval = htonl(value->audio_bitrate);
375 memcpy(iter, &lval, 4);
376 iter += 4;
377
378 /* Audio frame duration */
379 sval = htons(value->audio_frame_duration);
380 memcpy(iter, &sval, 2);
381 iter += 2;
382
383 /* Audio sample rate */
384 lval = htonl(value->audio_sample_rate);
385 memcpy(iter, &lval, 4);
386 iter += 4;
387
388 /* Audio channels */
389 lval = htonl(value->audio_channels);
390 memcpy(iter, &lval, 4);
391}
392
393void msi_msg_get_csettings ( MSIMessage *msg, MSICSettings *dest )
394{
395 if ( !msg || !dest || !msg->csettings.exists ) return;
396
397 dest->call_type = msg->csettings.value[0];
398 uint8_t *iter = msg->csettings.value + 1;
399
400 memcpy(&dest->video_bitrate, iter, 4);
401 iter += 4;
402 dest->video_bitrate = ntohl(dest->video_bitrate);
403
404 memcpy(&dest->max_video_width, iter, 2);
405 iter += 2;
406 dest->max_video_width = ntohs(dest->max_video_width);
407
408 memcpy(&dest->max_video_height, iter, 2);
409 iter += 2;
410 dest->max_video_height = ntohs(dest->max_video_height);
411
412 memcpy(&dest->audio_bitrate, iter, 4);
413 iter += 4;
414 dest->audio_bitrate = ntohl(dest->audio_bitrate);
415
416 memcpy(&dest->audio_frame_duration, iter, 2);
417 iter += 2;
418 dest->audio_frame_duration = ntohs(dest->audio_frame_duration);
419
420 memcpy(&dest->audio_sample_rate, iter, 4);
421 iter += 4;
422 dest->audio_sample_rate = ntohl(dest->audio_sample_rate);
423
424 memcpy(&dest->audio_channels, iter, 4);
425 dest->audio_channels = ntohl(dest->audio_channels);
426}
427
428typedef struct _Timer {
429 void (*func)(struct _Timer *);
430 uint64_t timeout;
431 MSISession *session;
432 int call_idx;
433 int id;
434
435} Timer;
436
437typedef struct _TimerHandler {
438 Timer **timers;
439
440 uint32_t max_capacity;
441 uint32_t size;
442} TimerHandler;
443
444
445static int timer_alloc (MSISession *session , void (*func)(Timer *), int call_idx, uint32_t timeout)
446{
447 static int timer_id;
448 TimerHandler *timer_handler = session->timer_handler;
449
450 uint32_t i = 0;
451
452 for (; i < timer_handler->max_capacity && timer_handler->timers[i]; i ++);
453
454 if (i == timer_handler->max_capacity) {
455 LOGGER_WARNING("Maximum capacity reached!");
456 return -1;
457 }
458
459 Timer *timer = timer_handler->timers[i] = calloc(sizeof(Timer), 1);
460
461 if (timer == NULL) {
462 LOGGER_ERROR("Failed to allocate timer!");
463 return -1;
464 }
465
466 timer_handler->size ++;
467
468 timer->func = func;
469 timer->session = session;
470 timer->call_idx = call_idx;
471 timer->timeout = timeout + current_time_monotonic(); /* In ms */
472 ++timer_id;
473 timer->id = timer_id;
474
475 /* reorder */
476 if (i) {
477 int64_t j = i - 1;
478
479 for (; j >= 0 && timeout < timer_handler->timers[j]->timeout; j--) {
480 Timer *tmp = timer_handler->timers[j];
481 timer_handler->timers[j] = timer;
482 timer_handler->timers[j + 1] = tmp;
483 }
484 }
485
486 LOGGER_DEBUG("Allocated timer index: %ull timeout: %ull, current size: %ull", i, timeout, timer_handler->size);
487 return timer->id;
488}
489
490static int timer_release ( TimerHandler *timers_container, int id)
491{
492 Timer **timed_events = timers_container->timers;
493
494 uint32_t i;
495 int rc = -1;
496
497 for (i = 0; i < timers_container->max_capacity; ++i) {
498 if (timed_events[i] && timed_events[i]->id == id) {
499 rc = i;
500 break;
501 }
502 }
503
504 if (rc == -1) {
505 LOGGER_WARNING("No event with id: %d", id);
506 return -1; 419 return -1;
507 } 420 }
508 421
509 free(timed_events[rc]); 422 if (msg->error.exists) {
510 423 uint8_t cast = msg->error.value;
511 timed_events[rc] = NULL; 424 it = msg_parse_header_out(IDError, it, &cast,
512 425 sizeof(cast), &size);
513 i = rc + 1; 426 }
514 427
515 for (; i < timers_container->max_capacity && timed_events[i]; i ++) { 428 if (msg->capabilities.exists) {
516 timed_events[i - 1] = timed_events[i]; 429 it = msg_parse_header_out(IDCapabilities, it, &msg->capabilities.value,
517 timed_events[i] = NULL; 430 sizeof(msg->capabilities.value), &size);
518 } 431 }
519 432
520 timers_container->size--; 433 if (msg->vfpsz.exists) {
521 434 uint16_t nb_vfpsz = htons(msg->vfpsz.value);
522 LOGGER_DEBUG("Popped id: %d, current size: %ull ", id, timers_container->size); 435 it = msg_parse_header_out(IDVFPSZ, it, &nb_vfpsz,
523 return 0; 436 sizeof(nb_vfpsz), &size);
524} 437 }
525 438
526/** 439 if ( it == parsed ) {
527 * Generate _random_ alphanumerical string. 440 LOGGER_WARNING("Parsing message failed; empty message");
528 */
529static void t_randomstr ( uint8_t *str, uint32_t size )
530{
531 if (str == NULL) {
532 LOGGER_DEBUG("Empty destination!");
533 return;
534 }
535
536 static const uint8_t _bytes[] =
537 "0123456789"
538 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
539 "abcdefghijklmnopqrstuvwxyz";
540
541 uint32_t _it = 0;
542
543 for ( ; _it < size; _it++ ) {
544 str[_it] = _bytes[ random_int() % 61 ];
545 }
546}
547
548/* TODO: it would be nice to actually have some sane error codes */
549typedef enum {
550 error_none,
551 error_deadcall, /* has call id but it's from old call */
552 error_id_mismatch, /* non-existing call */
553
554 error_no_callid, /* not having call id */
555 error_no_call, /* no call in session */
556 error_no_crypto_key, /* no crypto key */
557
558 error_busy
559
560} MSICallError; /* Error codes */
561
562
563/**
564 * Stringify error code.
565 */
566static const uint8_t *stringify_error ( MSICallError error_code )
567{
568 static const uint8_t *strings[] = {
569 ( uint8_t *) "",
570 ( uint8_t *) "Using dead call",
571 ( uint8_t *) "Call id not set to any call",
572 ( uint8_t *) "Call id not available",
573 ( uint8_t *) "No active call in session",
574 ( uint8_t *) "No Crypto-key set",
575 ( uint8_t *) "Callee busy"
576 };
577
578 return strings[error_code];
579}
580
581static int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t to )
582{
583 msi_msg_set_callid ( msg, call->id );
584
585 uint8_t msg_string_final [MSI_MAXMSG_SIZE];
586 uint16_t length = parse_send ( msg, msg_string_final );
587
588 if (!length) {
589 LOGGER_WARNING("Parsing message failed; nothing sent!");
590 return -1; 441 return -1;
591 } 442 }
592 443
593 if ( m_msi_packet(session->messenger_handle, to, msg_string_final, length) ) { 444 *it = 0;
445 size ++;
446
447 if ( m_msi_packet(m, friend_number, parsed, size) ) {
594 LOGGER_DEBUG("Sent message"); 448 LOGGER_DEBUG("Sent message");
595 return 0; 449 return 0;
596 } 450 }
597 451
598 return -1; 452 return -1;
599} 453}
600 454int send_error ( Messenger* m, uint32_t friend_number, MSIError error )
601static int send_reponse ( MSISession *session, MSICall *call, MSIResponse response, uint32_t to )
602{ 455{
603 MSIMessage *msg = msi_new_message ( TypeResponse, response ); 456 /* Send error message */
604 int ret = send_message ( session, call, msg, to ); 457 assert(m);
605 free ( msg ); 458
606 return ret; 459 LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_number);
607}
608
609static int send_error ( MSISession *session, MSICall *call, MSICallError errid, uint32_t to )
610{
611 if (!call) {
612 LOGGER_WARNING("Cannot handle error on 'null' call");
613 return -1;
614 }
615
616 LOGGER_DEBUG("Sending error: %d on call: %s", errid, call->id);
617
618 MSIMessage *msg_error = msi_new_message ( TypeResponse, error );
619
620 msi_msg_set_reason ( msg_error, stringify_error(errid) );
621 send_message ( session, call, msg_error, to );
622 free ( msg_error );
623 460
461 MSIMessage msg;
462 msg_init(&msg, requ_pop);
463
464 msg.error.exists = true;
465 msg.error.value = error;
466
467 send_message ( m, friend_number, &msg );
624 return 0; 468 return 0;
625} 469}
626 470int invoke_callback(MSICall* call, MSICallbackID cb)
627/**
628 * Determine 'bigger' call id
629 */
630static int call_id_bigger( const uint8_t *first, const uint8_t *second)
631{
632 return (memcmp(first, second, sizeof(MSICallIDType)) < 0);
633}
634
635
636/**
637 * Set/change peer csettings
638 */
639static int flush_peer_csettings ( MSICall *call, MSIMessage *msg, int peer_id )
640{ 471{
641 if ( msg->csettings.exists ) { 472 assert(call);
642 msi_msg_get_csettings(msg, &call->csettings_peer[peer_id]); 473
643 474 if ( call->session->callbacks[cb] ) {
644 LOGGER_DEBUG("Peer: %d \n" 475 LOGGER_DEBUG("Invoking callback function: %d", cb);
645 "Type: %u \n" 476 if ( call->session->callbacks[cb] ( call->session->av, call ) != 0 ) {
646 "Video bitrate: %u \n" 477 LOGGER_WARNING("Callback state handling failed, sending error");
647 "Video height: %u \n" 478 goto FAILURE;
648 "Video width: %u \n" 479 }
649 "Audio bitrate: %u \n" 480
650 "Audio framedur: %u \n"
651 "Audio sample rate: %u \n"
652 "Audio channels: %u \n", peer_id,
653 call->csettings_peer[peer_id].call_type,
654 call->csettings_peer[peer_id].video_bitrate,
655 call->csettings_peer[peer_id].max_video_height,
656 call->csettings_peer[peer_id].max_video_width,
657 call->csettings_peer[peer_id].audio_bitrate,
658 call->csettings_peer[peer_id].audio_frame_duration,
659 call->csettings_peer[peer_id].audio_sample_rate,
660 call->csettings_peer[peer_id].audio_channels );
661
662 return 0; 481 return 0;
663 } 482 }
664 483
665 LOGGER_WARNING("No csettings header!"); 484FAILURE:
485 /* If no callback present or error happened while handling,
486 * an error message will be sent to friend
487 */
488
489 if (call->error == msi_ENone)
490 call->error = msi_EHandle;
666 return -1; 491 return -1;
667} 492}
668 493static MSICall *get_call ( MSISession *session, uint32_t friend_number )
669
670/**
671 * Add peer to peer list.
672 */
673static void add_peer( MSICall *call, int peer_id )
674{ 494{
675 uint32_t *peers = !call->peers ? peers = calloc(sizeof(uint32_t), 1) : 495 assert(session);
676 realloc( call->peers, sizeof(uint32_t) * call->peer_count); 496
677 497 if (session->calls == NULL || session->calls_tail < friend_number)
678 if (!peers) { 498 return NULL;
679 LOGGER_WARNING("Allocation failed! Program might misbehave!"); 499
680 return; 500 return session->calls[friend_number];
681 }
682
683 call->peer_count ++;
684 call->peers = peers;
685 call->peers[call->peer_count - 1] = peer_id;
686
687 LOGGER_DEBUG("Added peer: %d", peer_id);
688}
689
690
691static MSICall *find_call ( MSISession *session, uint8_t *call_id )
692{
693 if ( call_id == NULL ) return NULL;
694
695 int32_t i = 0;
696
697 for (; i < session->max_calls; i ++ )
698 if ( session->calls[i] && memcmp(session->calls[i]->id, call_id, sizeof(session->calls[i]->id)) == 0 ) {
699 return session->calls[i];
700 }
701
702 return NULL;
703} 501}
704 502MSICall *new_call ( MSISession *session, uint32_t friend_number )
705static MSICall *init_call ( MSISession *session, int peers, int ringing_timeout )
706{ 503{
707 504 assert(session);
708 if (peers == 0) { 505
709 LOGGER_ERROR("No peers!"); 506 MSICall *rc = calloc(sizeof(MSICall), 1);
507
508 if (rc == NULL)
710 return NULL; 509 return NULL;
711 } 510
712 511 rc->session = session;
713 int32_t call_idx = 0; 512 rc->friend_number = friend_number;
714 513
715 for (; call_idx < session->max_calls; call_idx ++) { 514 if (session->calls == NULL) { /* Creating */
716 if ( !session->calls[call_idx] ) { 515 session->calls = calloc (sizeof(MSICall*), friend_number + 1);
717 516
718 if (!(session->calls[call_idx] = calloc ( sizeof ( MSICall ), 1 ))) { 517 if (session->calls == NULL) {
719 LOGGER_WARNING("Allocation failed! Program might misbehave!"); 518 free(rc);
720 return NULL; 519 return NULL;
721 }
722
723 break;
724 } 520 }
725 } 521
726 522 session->calls_tail = session->calls_head = friend_number;
727 if ( call_idx == session->max_calls ) { 523
728 LOGGER_WARNING("Reached maximum amount of calls!"); 524 } else if (session->calls_tail < friend_number) { /* Appending */
729 return NULL; 525 void* tmp = realloc(session->calls, sizeof(MSICall*) * friend_number + 1);
730 } 526
731 527 if (tmp == NULL) {
732 528 free(rc);
733 MSICall *call = session->calls[call_idx]; 529 return NULL;
734 530 }
735 call->call_idx = call_idx; 531
736 532 session->calls = tmp;
737 if ( !(call->csettings_peer = calloc ( sizeof ( MSICSettings ), peers )) ) { 533
738 LOGGER_WARNING("Allocation failed! Program might misbehave!"); 534 /* Set fields in between to null */
739 free(call); 535 uint32_t i = session->calls_tail + 1;
740 return NULL; 536 for (; i < friend_number; i ++)
741 } 537 session->calls[i] = NULL;
742 538
743 call->session = session; 539 rc->prev = session->calls[session->calls_tail];
744 540 session->calls[session->calls_tail]->next = rc;
745 call->request_timer_id = 0; 541
746 call->ringing_timer_id = 0; 542 session->calls_tail = friend_number;
747 543
748 call->ringing_tout_ms = ringing_timeout; 544 } else if (session->calls_head > friend_number) { /* Inserting at front */
749 545 rc->next = session->calls[session->calls_head];
750 LOGGER_DEBUG("Started new call with index: %u", call_idx); 546 session->calls[session->calls_head]->prev = rc;
751 return call; 547 session->calls_head = friend_number;
752} 548 }
753 549
754static int terminate_call ( MSISession *session, MSICall *call ) 550 session->calls[friend_number] = rc;
755{ 551 return rc;
756 if ( !call ) { 552}
757 LOGGER_WARNING("Tried to terminate non-existing call!"); 553void kill_call ( MSICall *call )
758 return -1; 554{
759 } 555 /* Assume that session mutex is locked */
760 556 if ( call == NULL )
761 /* Check event loop and cancel timed events if there are any 557 return;
762 */ 558
763 timer_release ( session->timer_handler, call->request_timer_id); 559 LOGGER_DEBUG("Killing call: %p", call);
764 timer_release ( session->timer_handler, call->ringing_timer_id); 560
765 561 MSISession* session = call->session;
766 session->calls[call->call_idx] = NULL; 562
767 563 MSICall* prev = call->prev;
768 LOGGER_DEBUG("Terminated call id: %d", call->call_idx); 564 MSICall* next = call->next;
769 565
770 free ( call->csettings_peer ); 566 if (prev)
771 free ( call->peers ); 567 prev->next = next;
772 free ( call ); 568 else if (next)
773 569 session->calls_head = next->friend_number;
774 return 0; 570 else goto CLEAR_CONTAINER;
775} 571
776 572 if (next)
777static void handle_remote_connection_change(Messenger *messenger, uint32_t friend_num, uint8_t status, void *session_p) 573 next->prev = prev;
778{ 574 else if (prev)
779 (void)messenger; 575 session->calls_tail = prev->friend_number;
780 MSISession *session = session_p; 576 else goto CLEAR_CONTAINER;
577
578 session->calls[call->friend_number] = NULL;
579 free(call);
580 return;
581
582CLEAR_CONTAINER:
583 session->calls_head = session->calls_tail = 0;
584 free(session->calls);
585 free(call);
586 session->calls = NULL;
587}
588void on_peer_status(Messenger* m, uint32_t friend_number, uint8_t status, void* data)
589{
590 (void)m;
591 MSISession *session = data;
781 592
782 switch ( status ) { 593 switch ( status ) {
783 case 0: { /* Went offline */ 594 case 0: { /* Friend is now offline */
784 int32_t j = 0; 595 LOGGER_DEBUG("Friend %d is now offline", friend_number);
785 596
786 for ( ; j < session->max_calls; j ++ ) { 597 pthread_mutex_lock(session->mutex);
787 598 MSICall* call = get_call(session, friend_number);
788 if ( !session->calls[j] ) continue; 599
789 600 if (call == NULL) {
790 uint16_t i = 0; 601 pthread_mutex_unlock(session->mutex);
791 602 return;
792 for ( ; i < session->calls[j]->peer_count; i ++ )
793 if ( session->calls[j]->peers[i] == (uint32_t)friend_num ) {
794 invoke_callback(session, j, msi_OnPeerTimeout);
795 terminate_call(session, session->calls[j]);
796 LOGGER_DEBUG("Remote: %d timed out!", friend_num);
797 return; /* TODO: On group calls change behaviour */
798 }
799 } 603 }
604
605 invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */
606 kill_call(call);
607 pthread_mutex_unlock(session->mutex);
800 } 608 }
801 break; 609 break;
802 610
@@ -804,811 +612,186 @@ static void handle_remote_connection_change(Messenger *messenger, uint32_t frien
804 break; 612 break;
805 } 613 }
806} 614}
807 615void handle_push ( MSICall *call, const MSIMessage *msg )
808/**
809 * Function called at request timeout
810 */
811static void handle_timeout ( Timer *timer )
812{ 616{
813 /* TODO: Cancel might not arrive there; set up 617 assert(call);
814 * timers on these cancels and terminate call on 618
815 * their timeout 619 LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_number);
816 */
817 MSICall *call = timer->session->calls[timer->call_idx];
818
819
820 if (call) {
821 LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx);
822 620
823 invoke_callback(timer->session, timer->call_idx, msi_OnRequestTimeout); 621 if (!msg->capabilities.exists) {
824 msi_cancel(timer->session, timer->call_idx, call->peers [0], "Request timed out"); 622 LOGGER_WARNING("Session: %p Invalid capabilities on 'push'");
623 call->error = msi_EInvalidMessage;
624 goto FAILURE;
825 } 625 }
826} 626
827 627 if (call->state != msi_CallActive) {
828 628 if (!msg->vfpsz.exists) {
829/********** Request handlers **********/ 629 LOGGER_WARNING("Session: %p Invalid vfpsz on 'push'");
830static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *msg ) 630 call->error = msi_EInvalidMessage;
831{ 631 goto FAILURE;
832 LOGGER_DEBUG("Session: %p Handling 'invite' on call: %d", session, call ? call->call_idx : -1); 632 }
833 633
834 634 call->peer_vfpsz = msg->vfpsz.value;
835 if (!msg->csettings.exists) {/**/ 635 }
836 LOGGER_WARNING("Peer sent invalid codec settings!"); 636
837 send_error ( session, call, error_no_callid, msg->friend_id ); 637
838 return 0; 638 switch (call->state) {
839 } 639 case msi_CallInactive: {
840 640 LOGGER_INFO("Friend is calling us");
841 if ( call ) { 641
842 if ( call->peers[0] == (uint32_t)msg->friend_id ) { 642 /* Call requested */
843 if (call->state == msi_CallInviting) { 643 call->peer_capabilities = msg->capabilities.value;
844 /* The glare case. A calls B when at the same time 644 call->state = msi_CallRequested;
845 * B calls A. Who has advantage is set bey calculating 645
846 * 'bigger' Call id and then that call id is being used in 646 if ( invoke_callback(call, msi_OnInvite) == -1 )
847 * future. User with 'bigger' Call id has the advantage 647 goto FAILURE;
848 * as in he will wait the response from the other. 648
649 } break;
650
651 case msi_CallActive: {
652 if (msg->vfpsz.exists) {
653 /* If peer sended video frame piece size
654 * while the call is already active it's probable
655 * that he is trying to re-call us while the call
656 * is not terminated on our side. We can assume that
657 * in this case we can automatically answer the re-call.
849 */ 658 */
850 LOGGER_DEBUG("Glare case; Peer: %d", call->peers[0]); 659 if (call->peer_vfpsz != msg->vfpsz.value) {
851 660 LOGGER_WARNING("Friend sent invalid parameters for re-call");
852 if ( call_id_bigger (call->id, msg->callid.value) == 1 ) { /* Peer has advantage */ 661 call->error = msi_EInvalidParam;
853 662 invoke_callback(call, msi_OnError);
854 /* Terminate call; peer will timeout(call) if call initialization fails */ 663 goto FAILURE;
855 terminate_call(session, call);
856
857 call = init_call ( session, 1, 0 );
858
859 if ( !call ) {
860 LOGGER_ERROR("Starting call");
861 return 0;
862 }
863
864 } else {
865 return 0; /* Wait for ringing from peer */
866 }
867 } else if (call->state == msi_CallActive) {
868 /* Request for media change; call callback and send starting response */
869 if (flush_peer_csettings(call, msg, 0) != 0) { /**/
870 LOGGER_WARNING("Peer sent invalid csetting!");
871 send_error ( session, call, error_no_callid, msg->friend_id );
872 return 0;
873 } 664 }
874 665
875 LOGGER_DEBUG("Set new call type: %s", call->csettings_peer[0].call_type == msi_TypeAudio ? "audio" : "video"); 666 LOGGER_INFO("Friend is recalling us");
876 send_reponse(session, call, starting, msg->friend_id); 667
877 invoke_callback(session, call->call_idx, msi_OnPeerCSChange); 668 MSIMessage msg;
878 return 1; 669 msg_init(&msg, requ_push);
670
671 msg.capabilities.exists = true;
672 msg.capabilities.value = call->self_capabilities;
673
674 msg.vfpsz.exists = true;
675 msg.vfpsz.value = VIDEOFRAME_PIECE_SIZE;
676
677 send_message ( call->session->messenger, call->friend_number, &msg );
678
679 /* If peer changed capabilities during re-call they will
680 * be handled accordingly during the next step
681 */
879 } 682 }
880 } else { 683
881 send_error ( session, call, error_busy, msg->friend_id ); /* TODO: Ugh*/ 684 /* Only act if capabilities changed */
882 terminate_call(session, call); 685 if ( call->peer_capabilities != msg->capabilities.value) {
883 return 0; 686 LOGGER_INFO("Friend is changing capabilities to: %u", msg->capabilities.value);
884 } 687
885 } else { 688 call->peer_capabilities = msg->capabilities.value;
886 call = init_call ( session, 1, 0 ); 689 if ( invoke_callback(call, msi_OnCapabilities) == -1 )
887 690 goto FAILURE;
888 if ( !call ) { 691 }
889 LOGGER_ERROR("Starting call"); 692 } break;
890 return 0; 693
891 } 694 case msi_CallRequesting: {
892 } 695 LOGGER_INFO("Friend answered our call");
893 696
894 if ( !msg->callid.exists ) { 697 /* Call started */
895 send_error ( session, call, error_no_callid, msg->friend_id ); 698 call->peer_capabilities = msg->capabilities.value;
896 terminate_call(session, call); 699 call->state = msi_CallActive;
897 return 0; 700
898 } 701 if ( invoke_callback(call, msi_OnStart) == -1 )
899 702 goto FAILURE;
900 memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) ); 703
901 call->state = msi_CallStarting; 704 } break;
902 705
903 add_peer( call, msg->friend_id); 706 case msi_CallRequested: {
904 flush_peer_csettings ( call, msg, 0 ); 707 /* Consecutive pushes during initialization state are ignored */
905 send_reponse(session, call, ringing, msg->friend_id); 708 LOGGER_WARNING("Consecutive push");
906 invoke_callback(session, call->call_idx, msi_OnInvite); 709 } break;
907 710 }
908 return 1; 711
909} 712 return;
910 713
911static int handle_recv_start ( MSISession *session, MSICall *call, MSIMessage *msg ) 714FAILURE:
912{ 715 send_error(call->session->messenger, call->friend_number, call->error);
913 if ( !call ) { 716 kill_call(call);
914 LOGGER_WARNING("Session: %p Handling 'start' on no call"); 717}
915 return 0; 718void handle_pop ( MSICall *call, const MSIMessage *msg )
916 } 719{
917 720 assert(call);
918 (void)msg; 721
919 722 LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_number);
920 LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id ); 723
921 724 /* callback errors are ignored */
922 call->state = msi_CallActive; 725
923 invoke_callback(session, call->call_idx, msi_OnStart); 726 if (msg->error.exists) {
924 return 1; 727 LOGGER_WARNING("Friend detected an error: %d", msg->error.value);
925} 728 call->error = msg->error.value;
926 729 invoke_callback(call, msi_OnError);
927static int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage *msg ) 730
928{ 731 } else switch (call->state) {
929 if ( !call ) { 732 case msi_CallInactive: {
930 LOGGER_WARNING("Session: %p Handling 'start' on no call"); 733 LOGGER_ERROR("Handling what should be impossible case");
931 return 0; 734 abort();
932 } 735 } break;
933 736
934 LOGGER_DEBUG("Session: %p Handling 'reject' on call: %u", session, call->call_idx); 737 case msi_CallActive: {
935 738 /* Hangup */
936 invoke_callback(session, call->call_idx, msi_OnReject); 739 LOGGER_INFO("Friend hung up on us");
937 740 invoke_callback(call, msi_OnEnd);
938 send_reponse(session, call, ending, msg->friend_id); 741 } break;
939 terminate_call(session, call); 742
940 743 case msi_CallRequesting: {
941 return 1; 744 /* Reject */
942} 745 LOGGER_INFO("Friend rejected our call");
943 746 invoke_callback(call, msi_OnEnd);
944static int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage *msg ) 747 } break;
945{ 748
946 if ( !call ) { 749 case msi_CallRequested: {
947 LOGGER_WARNING("Session: %p Handling 'start' on no call"); 750 /* Cancel */
948 return 0; 751 LOGGER_INFO("Friend canceled call invite");
949 } 752 invoke_callback(call, msi_OnEnd);
950 753 } break;
951 (void)msg; 754 }
952 755
953 LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx); 756 kill_call ( call );
954 757}
955 invoke_callback(session, call->call_idx, msi_OnCancel); 758void handle_msi_packet ( Messenger* m, uint32_t friend_number, const uint8_t* data, uint16_t length, void* object )
956 terminate_call ( session, call );
957
958 return 1;
959}
960
961static int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg )
962{
963 if ( !call ) {
964 LOGGER_WARNING("Session: %p Handling 'start' on no call");
965 return 0;
966 }
967
968 LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx);
969
970 invoke_callback(session, call->call_idx, msi_OnEnd);
971 send_reponse(session, call, ending, msg->friend_id);
972 terminate_call ( session, call );
973
974 return 1;
975}
976
977/********** Response handlers **********/
978static int handle_recv_ringing ( MSISession *session, MSICall *call, MSIMessage *msg )
979{
980 if ( !call ) {
981 LOGGER_WARNING("Session: %p Handling 'start' on no call");
982 return 0;
983 }
984
985 (void)msg;
986
987 if ( call->ringing_timer_id ) {
988 LOGGER_WARNING("Call already ringing");
989 return 0;
990 }
991
992 LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %d", session, call->call_idx );
993
994 call->ringing_timer_id = timer_alloc
995 ( session, handle_timeout, call->call_idx, call->ringing_tout_ms );
996 invoke_callback(session, call->call_idx, msi_OnRinging);
997 return 1;
998}
999static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg )
1000{
1001 if ( !call ) {
1002 LOGGER_WARNING("Session: %p Handling 'starting' on non-existing call");
1003 return 0;
1004 }
1005
1006 if ( call->state == msi_CallActive ) { /* Change media */
1007
1008 LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx );
1009
1010 invoke_callback(session, call->call_idx, msi_OnSelfCSChange);
1011
1012 } else if ( call->state == msi_CallInviting ) {
1013 LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx );
1014
1015 call->state = msi_CallActive;
1016
1017 MSIMessage *msg_start = msi_new_message ( TypeRequest, start );
1018 send_message ( session, call, msg_start, msg->friend_id );
1019 free ( msg_start );
1020
1021
1022 flush_peer_csettings ( call, msg, 0 );
1023
1024 /* This is here in case of glare */
1025 timer_release(session->timer_handler, call->ringing_timer_id);
1026 invoke_callback(session, call->call_idx, msi_OnStart);
1027 } else {
1028 LOGGER_ERROR("Invalid call state");
1029 terminate_call(session, call );
1030 return 0;
1031 }
1032
1033 return 1;
1034}
1035static int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage *msg )
1036{
1037 if ( !call ) {
1038 LOGGER_WARNING("Session: %p Handling 'start' on no call");
1039 return 0;
1040 }
1041
1042 (void)msg;
1043
1044 LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx );
1045
1046 invoke_callback(session, call->call_idx, msi_OnEnd);
1047 terminate_call ( session, call );
1048
1049 return 1;
1050}
1051static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg )
1052{
1053 if ( !call ) {
1054 LOGGER_WARNING("Handling 'error' on non-existing call!");
1055 return -1;
1056 }
1057
1058 LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx );
1059
1060 invoke_callback(session, call->call_idx, msi_OnEnd);
1061
1062 /* Handle error accordingly */
1063 if ( msg->reason.exists ) {
1064 /* TODO */
1065 }
1066
1067 terminate_call ( session, call );
1068
1069 return 1;
1070}
1071
1072/**
1073 * BASIC call flow:
1074 *
1075 * ALICE BOB
1076 * | invite --> |
1077 * | |
1078 * | <-- ringing |
1079 * | |
1080 * | <-- starting |
1081 * | |
1082 * | start --> |
1083 * | |
1084 * | <-- MEDIA TRANS --> |
1085 * | |
1086 * | end --> |
1087 * | |
1088 * | <-- ending |
1089 *
1090 * Alice calls Bob by sending invite packet.
1091 * Bob recvs the packet and sends an ringing packet;
1092 * which notifies Alice that her invite is acknowledged.
1093 * Ringing screen shown on both sides.
1094 * Bob accepts the invite for a call by sending starting packet.
1095 * Alice recvs the starting packet and sends the started packet to
1096 * inform Bob that she recved the starting packet.
1097 * Now the media transmission is established ( i.e. RTP transmission ).
1098 * Alice hangs up and sends end packet.
1099 * Bob recves the end packet and sends ending packet
1100 * as the acknowledgement that the call is ending.
1101 *
1102 *
1103 */
1104static void msi_handle_packet ( Messenger *messenger, uint32_t source, const uint8_t *data, uint16_t length,
1105 void *object )
1106{ 759{
1107 LOGGER_DEBUG("Got msi message"); 760 LOGGER_DEBUG("Got msi message");
1108 /* Unused */ 761
1109 (void)messenger;
1110
1111 MSISession *session = object; 762 MSISession *session = object;
1112 MSIMessage *msg; 763 MSIMessage msg;
1113 764
1114 if ( !length ) { 765 if ( msg_parse_in ( &msg, data, length ) == -1 ) {
1115 LOGGER_WARNING("Length param negative");
1116 return;
1117 }
1118
1119 msg = parse_recv ( data, length );
1120
1121 if ( !msg ) {
1122 LOGGER_WARNING("Error parsing message"); 766 LOGGER_WARNING("Error parsing message");
767 send_error(m, friend_number, msi_EInvalidMessage);
1123 return; 768 return;
1124 } else { 769 } else {
1125 LOGGER_DEBUG("Successfully parsed message"); 770 LOGGER_DEBUG("Successfully parsed message");
1126 } 771 }
1127 772
1128 msg->friend_id = source;
1129
1130 pthread_mutex_lock(session->mutex);
1131
1132 /* Find what call */
1133 MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL;
1134
1135 /* Now handle message */
1136
1137 if ( msg->request.exists ) { /* Handle request */
1138
1139 switch (msg->request.value) {
1140 case invite:
1141 handle_recv_invite ( session, call, msg );
1142 break;
1143
1144 case start:
1145 handle_recv_start ( session, call, msg );
1146 break;
1147
1148 case cancel:
1149 handle_recv_cancel ( session, call, msg );
1150 break;
1151
1152 case reject:
1153 handle_recv_reject ( session, call, msg );
1154 break;
1155
1156 case end:
1157 handle_recv_end ( session, call, msg );
1158 break;
1159 }
1160
1161 } else if ( msg->response.exists ) { /* Handle response */
1162
1163 /* Got response so cancel timer */
1164 if ( call ) timer_release(session->timer_handler, call->request_timer_id);
1165
1166 switch (msg->response.value) {
1167 case ringing:
1168 handle_recv_ringing ( session, call, msg );
1169 break;
1170
1171 case starting:
1172 handle_recv_starting ( session, call, msg );
1173 break;
1174
1175 case ending:
1176 handle_recv_ending ( session, call, msg );
1177 break;
1178
1179 case error:
1180 handle_recv_error ( session, call, msg );
1181 break;
1182 }
1183
1184 } else {
1185 LOGGER_WARNING("Invalid message: no resp nor requ headers");
1186 }
1187
1188 free ( msg );
1189
1190 pthread_mutex_unlock(session->mutex);
1191}
1192
1193
1194
1195/********** User functions **********/
1196void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata )
1197{
1198 session->callbacks[id].first = callback;
1199 session->callbacks[id].second = userdata;
1200}
1201
1202
1203MSISession *msi_new ( Messenger *messenger, int32_t max_calls )
1204{
1205 if (messenger == NULL) {
1206 LOGGER_ERROR("Could not init session on empty messenger!");
1207 return NULL;
1208 }
1209
1210 if ( !max_calls ) {
1211 LOGGER_WARNING("Invalid max call treshold!");
1212 return NULL;
1213 }
1214
1215 MSISession *retu = calloc ( sizeof ( MSISession ), 1 );
1216
1217 if (retu == NULL) {
1218 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1219 return NULL;
1220 }
1221
1222 if (!(retu->calls = calloc( sizeof (MSICall *), max_calls ))) {
1223 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1224 goto error;
1225 }
1226
1227 retu->timer_handler = calloc(1, sizeof(TimerHandler));
1228
1229 if (retu->timer_handler == NULL) {
1230 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1231 goto error;
1232 }
1233
1234 /* Allocate space for timers */
1235 ((TimerHandler *)retu->timer_handler)->max_capacity = max_calls * 10;
1236
1237 if (!(((TimerHandler *)retu->timer_handler)->timers = calloc(max_calls * 10, sizeof(Timer *)))) {
1238 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1239 goto error;
1240 }
1241
1242 if (create_recursive_mutex(retu->mutex) != 0) {
1243 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
1244 goto error;
1245 }
1246
1247 retu->messenger_handle = messenger;
1248 retu->agent_handler = NULL;
1249 retu->max_calls = max_calls;
1250 retu->frequ = 10000; /* default value? */
1251 retu->call_timeout = 30000; /* default value? */
1252
1253 m_callback_msi_packet(messenger, msi_handle_packet, retu );
1254
1255 /* This is called when remote terminates session */
1256 m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, retu);
1257
1258 LOGGER_DEBUG("New msi session: %p max calls: %u", retu, max_calls);
1259 return retu;
1260
1261error:
1262
1263 if (retu->timer_handler) {
1264 free(((TimerHandler *)retu->timer_handler)->timers);
1265 free(retu->timer_handler);
1266 }
1267
1268 free(retu->calls);
1269 free(retu);
1270 return NULL;
1271}
1272
1273
1274int msi_kill ( MSISession *session )
1275{
1276 if (session == NULL) {
1277 LOGGER_ERROR("Tried to terminate non-existing session");
1278 return -1;
1279 }
1280
1281 m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL);
1282 pthread_mutex_lock(session->mutex); 773 pthread_mutex_lock(session->mutex);
1283 774 MSICall *call = get_call(session, friend_number);
1284 /* Cancel active calls */ 775
1285 int32_t idx = 0; 776 if (call == NULL) {
1286 777 if (msg.request.value != requ_push) {
1287 for (; idx < session->max_calls; idx ++) if ( session->calls[idx] ) { 778 send_error(m, friend_number, msi_EStrayMessage);
1288 /* Cancel all? */ 779 pthread_mutex_unlock(session->mutex);
1289 uint16_t _it = 0; 780 return;
1290 /*for ( ; _it < session->calls[idx]->peer_count; _it++ )
1291 * FIXME: will not work on multiple peers, must cancel call for all peers
1292 */
1293 MSICallState state = session->calls[idx]->state;
1294
1295 if (state == msi_CallInviting) {
1296 msi_cancel( session, idx, session->calls[idx]->peers [_it], "MSI session terminated!" );
1297 } else {
1298 msi_stopcall(session, idx);
1299 }
1300 } 781 }
1301 782
1302 free(((TimerHandler *)session->timer_handler)->timers); 783 call = new_call(session, friend_number);
1303 free(session->timer_handler); 784 if (call == NULL) {
1304 785 send_error(m, friend_number, msi_ESystem);
1305 free ( session->calls );
1306 pthread_mutex_unlock(session->mutex);
1307 pthread_mutex_destroy(session->mutex);
1308
1309 LOGGER_DEBUG("Terminated session: %p", session);
1310 free ( session );
1311 return 0;
1312}
1313
1314int msi_invite ( MSISession *session,
1315 int32_t *call_index,
1316 const MSICSettings *csettings,
1317 uint32_t rngsec,
1318 uint32_t friend_id )
1319{
1320 pthread_mutex_lock(session->mutex);
1321
1322 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id);
1323
1324
1325 int i = 0;
1326
1327 for (; i < session->max_calls; i ++)
1328 if (session->calls[i] && session->calls[i]->peers[0] == friend_id) {
1329 LOGGER_ERROR("Already in a call with friend %d", friend_id);
1330 pthread_mutex_unlock(session->mutex); 786 pthread_mutex_unlock(session->mutex);
1331 return msi_ErrorAlreadyInCallWithPeer; 787 return;
1332 } 788 }
1333
1334
1335 MSICall *call = init_call ( session, 1, rngsec ); /* Just one peer for now */
1336
1337 if ( !call ) {
1338 pthread_mutex_unlock(session->mutex);
1339 LOGGER_ERROR("Cannot handle more calls");
1340 return msi_ErrorReachedCallLimit;
1341 }
1342
1343 *call_index = call->call_idx;
1344
1345 t_randomstr ( call->id, sizeof(call->id) );
1346
1347 add_peer ( call, friend_id );
1348
1349 call->csettings_local = *csettings;
1350
1351 MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite );
1352
1353 msi_msg_set_csettings(msg_invite, csettings);
1354 send_message ( session, call, msg_invite, friend_id );
1355 free( msg_invite );
1356
1357 call->state = msi_CallInviting;
1358
1359 call->request_timer_id = timer_alloc ( session, handle_timeout, call->call_idx, m_deftout );
1360
1361 LOGGER_DEBUG("Invite sent");
1362
1363 pthread_mutex_unlock(session->mutex);
1364
1365 return 0;
1366}
1367
1368int msi_hangup ( MSISession *session, int32_t call_index )
1369{
1370 pthread_mutex_lock(session->mutex);
1371 LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index);
1372
1373 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1374 LOGGER_ERROR("Invalid call index!");
1375 pthread_mutex_unlock(session->mutex);
1376 return msi_ErrorNoCall;
1377 }
1378
1379 if ( session->calls[call_index]->state != msi_CallActive ) {
1380 LOGGER_ERROR("Call is not active!");
1381 pthread_mutex_unlock(session->mutex);
1382 return msi_ErrorInvalidState;
1383 } 789 }
1384 790
1385 MSIMessage *msg_end = msi_new_message ( TypeRequest, end ); 791 if (msg.request.value == requ_push)
1386 792 handle_push(call, &msg);
1387 /* hangup for each peer */ 793 else
1388 int it = 0; 794 handle_pop(call, &msg); /* always kills the call */
1389 795
1390 for ( ; it < session->calls[call_index]->peer_count; it ++ )
1391 send_message ( session, session->calls[call_index], msg_end, session->calls[call_index]->peers[it] );
1392
1393 session->calls[call_index]->state = msi_CallOver;
1394
1395 free ( msg_end );
1396
1397 session->calls[call_index]->request_timer_id =
1398 timer_alloc ( session, handle_timeout, call_index, m_deftout );
1399
1400 pthread_mutex_unlock(session->mutex);
1401 return 0;
1402}
1403
1404int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings )
1405{
1406 pthread_mutex_lock(session->mutex);
1407 LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index);
1408
1409 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1410 LOGGER_ERROR("Invalid call index!");
1411 pthread_mutex_unlock(session->mutex);
1412 return msi_ErrorNoCall;
1413 }
1414
1415 if ( session->calls[call_index]->state != msi_CallStarting ) {
1416 LOGGER_ERROR("Call is in invalid state!");
1417 pthread_mutex_unlock(session->mutex);
1418 return msi_ErrorInvalidState;
1419 }
1420
1421 MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting );
1422
1423 session->calls[call_index]->csettings_local = *csettings;
1424
1425 msi_msg_set_csettings(msg_starting, csettings);
1426
1427 send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] );
1428 free ( msg_starting );
1429
1430 session->calls[call_index]->state = msi_CallActive;
1431
1432 pthread_mutex_unlock(session->mutex);
1433 return 0;
1434}
1435
1436int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason )
1437{
1438 pthread_mutex_lock(session->mutex);
1439 LOGGER_DEBUG("Session: %p Canceling call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
1440
1441 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1442 LOGGER_ERROR("Invalid call index!");
1443 pthread_mutex_unlock(session->mutex);
1444 return msi_ErrorNoCall;
1445 }
1446
1447 if ( session->calls[call_index]->state != msi_CallInviting ) {
1448 LOGGER_ERROR("Call is in invalid state: %u", session->calls[call_index]->state);
1449 pthread_mutex_unlock(session->mutex);
1450 return msi_ErrorInvalidState;
1451 }
1452
1453 MSIMessage *msg_cancel = msi_new_message ( TypeRequest, cancel );
1454
1455 /* FIXME */
1456#if 0
1457
1458 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) {
1459 MSIReasonStrType reason_cast;
1460 memset(reason_cast, '\0', sizeof(MSIReasonStrType));
1461 memcpy(reason_cast, reason, strlen(reason));
1462 msi_msg_set_reason(msg_cancel, reason_cast);
1463 }
1464
1465#else
1466 (void)reason;
1467
1468#endif
1469
1470 send_message ( session, session->calls[call_index], msg_cancel, peer );
1471 free ( msg_cancel );
1472
1473 terminate_call ( session, session->calls[call_index] );
1474 pthread_mutex_unlock(session->mutex);
1475
1476 return 0;
1477}
1478
1479int msi_reject ( MSISession *session, int32_t call_index, const char *reason )
1480{
1481 pthread_mutex_lock(session->mutex);
1482 LOGGER_DEBUG("Session: %p Rejecting call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
1483
1484 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1485 LOGGER_ERROR("Invalid call index!");
1486 pthread_mutex_unlock(session->mutex);
1487 return msi_ErrorNoCall;
1488 }
1489
1490 if ( session->calls[call_index]->state != msi_CallStarting ) {
1491 LOGGER_ERROR("Call is in invalid state!");
1492 pthread_mutex_unlock(session->mutex);
1493 return msi_ErrorInvalidState;
1494 }
1495
1496 MSIMessage *msg_reject = msi_new_message ( TypeRequest, reject );
1497
1498 /* FIXME */
1499#if 0
1500
1501 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) {
1502 MSIReasonStrType reason_cast;
1503 memset(reason_cast, '\0', sizeof(MSIReasonStrType));
1504 memcpy(reason_cast, reason, strlen(reason));
1505 msi_msg_set_reason(msg_reject, reason_cast);
1506 }
1507
1508#else
1509 (void)reason;
1510
1511#endif
1512
1513 send_message ( session, session->calls[call_index], msg_reject,
1514 session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] );
1515 free ( msg_reject );
1516
1517 session->calls[call_index]->state = msi_CallOver;
1518 session->calls[call_index]->request_timer_id =
1519 timer_alloc ( session, handle_timeout, call_index, m_deftout );
1520
1521 pthread_mutex_unlock(session->mutex);
1522 return 0;
1523}
1524
1525int msi_stopcall ( MSISession *session, int32_t call_index )
1526{
1527 pthread_mutex_lock(session->mutex);
1528 LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index);
1529
1530 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1531 pthread_mutex_unlock(session->mutex);
1532 return msi_ErrorNoCall;
1533 }
1534
1535 /* just terminate it */
1536
1537 terminate_call ( session, session->calls[call_index] );
1538
1539 pthread_mutex_unlock(session->mutex);
1540 return 0;
1541}
1542
1543int msi_change_csettings(MSISession *session, int32_t call_index, const MSICSettings *csettings)
1544{
1545 pthread_mutex_lock(session->mutex);
1546
1547 LOGGER_DEBUG("Changing media on call: %d", call_index);
1548
1549 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1550 LOGGER_ERROR("Invalid call index!");
1551 pthread_mutex_unlock(session->mutex);
1552 return msi_ErrorNoCall;
1553 }
1554
1555 MSICall *call = session->calls[call_index];
1556
1557 if ( call->state != msi_CallActive ) {
1558 LOGGER_ERROR("Call is not active!");
1559 pthread_mutex_unlock(session->mutex);
1560 return msi_ErrorInvalidState;
1561 }
1562
1563 MSICSettings *local = &call->csettings_local;
1564
1565 if (
1566 local->call_type == csettings->call_type &&
1567 local->video_bitrate == csettings->video_bitrate &&
1568 local->max_video_width == csettings->max_video_width &&
1569 local->max_video_height == csettings->max_video_height &&
1570 local->audio_bitrate == csettings->audio_bitrate &&
1571 local->audio_frame_duration == csettings->audio_frame_duration &&
1572 local->audio_sample_rate == csettings->audio_sample_rate &&
1573 local->audio_channels == csettings->audio_channels ) {
1574 LOGGER_ERROR("Call is already set accordingly!");
1575 pthread_mutex_unlock(session->mutex);
1576 return -1;
1577 }
1578
1579 *local = *csettings;
1580
1581 MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite );
1582
1583 msi_msg_set_csettings ( msg_invite, local );
1584 send_message ( session, call, msg_invite, call->peers[0] );
1585 free ( msg_invite );
1586
1587 LOGGER_DEBUG("Request for media change sent");
1588
1589 pthread_mutex_unlock(session->mutex);
1590
1591 return 0;
1592}
1593
1594void msi_do(MSISession *session)
1595{
1596 pthread_mutex_lock(session->mutex);
1597
1598 TimerHandler *timer = session->timer_handler;
1599
1600 uint64_t time = current_time_monotonic();
1601
1602 while ( timer->timers[0] && timer->timers[0]->timeout < time ) {
1603 LOGGER_DEBUG("Executing timer assigned at: %d", timer->timers[0]->timeout);
1604
1605 int id = timer->timers[0]->id;
1606 timer->timers[0]->func(timer->timers[0]);
1607
1608 /* In case function has released timer */
1609 if (timer->timers[0] && timer->timers[0]->id == id)
1610 timer_release(timer, id);
1611 }
1612
1613 pthread_mutex_unlock(session->mutex); 796 pthread_mutex_unlock(session->mutex);
1614} 797}
diff --git a/toxav/msi.h b/toxav/msi.h
index 660df05e..59f32c1d 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,187 +19,136 @@
19 * 19 *
20 */ 20 */
21 21
22#ifndef __TOXMSI 22#ifndef MSI_H
23#define __TOXMSI 23#define MSI_H
24 24
25#include <inttypes.h> 25#include <inttypes.h>
26#include <pthread.h> 26#include <pthread.h>
27 27
28#include "codec.h" 28#include "audio.h"
29#include "video.h"
29#include "../toxcore/Messenger.h" 30#include "../toxcore/Messenger.h"
30 31
31typedef uint8_t MSICallIDType[12]; 32/** Preconfigured value for video splitting */
32typedef uint8_t MSIReasonStrType[255]; 33#define VIDEOFRAME_PIECE_SIZE 500
33typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx, void *arg );
34 34
35/** 35/**
36 * Call type identifier. Also used as rtp callback prefix. 36 * Error codes.
37 */ 37 */
38typedef enum { 38typedef enum {
39 msi_TypeAudio = 192, 39 msi_ENone,
40 msi_TypeVideo 40 msi_EInvalidMessage,
41} MSICallType; 41 msi_EInvalidParam,
42 42 msi_EInvalidState,
43 msi_EStrayMessage,
44 msi_ESystem,
45 msi_EHandle,
46 msi_EUndisclosed, /* NOTE: must be last enum otherwise parsing will not work */
47} MSIError;
43 48
44/** 49/**
45 * Call state identifiers. 50 * Supported capabilities
46 */ 51 */
47typedef enum { 52typedef enum {
48 msi_CallInviting, /* when sending call invite */ 53 msi_CapSAudio = 4, /* sending audio */
49 msi_CallStarting, /* when getting call invite */ 54 msi_CapSVideo = 8, /* sending video */
50 msi_CallActive, 55 msi_CapRAudio = 16, /* receiving audio */
51 msi_CallHold, 56 msi_CapRVideo = 32, /* receiving video */
52 msi_CallOver 57} MSICapabilities;
53
54} MSICallState;
55 58
56 59
57/** 60/**
58 * Encoding settings. 61 * Call state identifiers.
59 */ 62 */
60typedef struct _MSICodecSettings { 63typedef enum {
61 MSICallType call_type; 64 msi_CallInactive, /* Default */
62 65 msi_CallActive,
63 uint32_t video_bitrate; /* In kbits/s */ 66 msi_CallRequesting, /* when sending call invite */
64 uint16_t max_video_width; /* In px */ 67 msi_CallRequested, /* when getting call invite */
65 uint16_t max_video_height; /* In px */ 68} MSICallState;
66
67 uint32_t audio_bitrate; /* In bits/s */
68 uint16_t audio_frame_duration; /* In ms */
69 uint32_t audio_sample_rate; /* In Hz */
70 uint32_t audio_channels;
71} MSICSettings;
72
73 69
74/** 70/**
75 * Callbacks ids that handle the states 71 * Callbacks ids that handle the states
76 */ 72 */
77typedef enum { 73typedef enum {
78 msi_OnInvite, /* Incoming call */ 74 msi_OnInvite, /* Incoming call */
79 msi_OnRinging, /* When peer is ready to accept/reject the call */
80 msi_OnStart, /* Call (RTP transmission) started */ 75 msi_OnStart, /* Call (RTP transmission) started */
81 msi_OnCancel, /* The side that initiated call canceled invite */
82 msi_OnReject, /* The side that was invited rejected the call */
83 msi_OnEnd, /* Call that was active ended */ 76 msi_OnEnd, /* Call that was active ended */
84 msi_OnRequestTimeout, /* When the requested action didn't get response in specified time */ 77 msi_OnError, /* On protocol error */
85 msi_OnPeerTimeout, /* Peer timed out; stop the call */ 78 msi_OnPeerTimeout, /* Peer timed out; stop the call */
86 msi_OnPeerCSChange, /* Peer requested Csettings change */ 79 msi_OnCapabilities, /* Peer requested capabilities change */
87 msi_OnSelfCSChange /* Csettings change confirmation */
88} MSICallbackID; 80} MSICallbackID;
89 81
90/** 82/**
91 * Errors 83 * The call struct. Please do not modify outside msi.c
92 */
93typedef enum {
94 msi_ErrorNoCall = -20, /* Trying to perform call action while not in a call */
95 msi_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/
96 msi_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */
97 msi_ErrorReachedCallLimit = -23, /* Cannot handle more calls */
98} MSIError;
99
100/**
101 * The call struct.
102 */ 84 */
103typedef struct _MSICall { /* Call info structure */ 85typedef struct MSICall_s {
104 struct _MSISession *session; /* Session pointer */ 86 struct MSISession_s *session; /* Session pointer */
105
106 MSICallState state;
107
108 MSICSettings csettings_local; /* Local call settings */
109 MSICSettings *csettings_peer; /* Peers call settings */
110
111 MSICallIDType id; /* Random value identifying the call */
112
113 int ringing_tout_ms; /* Ringing timeout in ms */
114
115 int request_timer_id; /* Timer id for outgoing request/action */
116 int ringing_timer_id; /* Timer id for ringing timeout */
117
118 uint32_t *peers;
119 uint16_t peer_count;
120 87
121 int32_t call_idx; /* Index of this call in MSISession */ 88 MSICallState state;
89 uint8_t peer_capabilities; /* Peer capabilities */
90 uint8_t self_capabilities; /* Self capabilities */
91 uint16_t peer_vfpsz; /* Video frame piece size */
92 uint32_t friend_number; /* Index of this call in MSISession */
93 MSIError error; /* Last error */
94
95 void* av_call; /* Pointer to av call handler */
96
97 struct MSICall_s* next;
98 struct MSICall_s* prev;
122} MSICall; 99} MSICall;
123 100
124 101
125/** 102/**
126 * Control session struct 103 * Expected return on success is 0, if any other number is
104 * returned the call is considered errored and will be handled
105 * as such which means it will be terminated without any notice.
127 */ 106 */
128typedef struct _MSISession { 107typedef int msi_action_cb ( void *av, MSICall* call);
129 108
109/**
110 * Control session struct. Please do not modify outside msi.c
111 */
112typedef struct MSISession_s {
130 /* Call handlers */ 113 /* Call handlers */
131 MSICall **calls; 114 MSICall **calls;
132 int32_t max_calls; 115 uint32_t calls_tail;
133 116 uint32_t calls_head;
134 void *agent_handler; 117
135 Messenger *messenger_handle; 118 void *av;
136 119 Messenger *messenger;
137 uint32_t frequ;
138 uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */
139 120
140 pthread_mutex_t mutex[1]; 121 pthread_mutex_t mutex[1];
141 122 msi_action_cb* callbacks[7];
142 void *timer_handler;
143 PAIR(MSICallbackType, void *) callbacks[10];
144} MSISession; 123} MSISession;
145 124
146/** 125/**
147 * Start the control session. 126 * Start the control session.
148 */ 127 */
149MSISession *msi_new ( Messenger *messenger, int32_t max_calls ); 128MSISession *msi_new ( Messenger *m );
150
151/** 129/**
152 * Terminate control session. 130 * Terminate control session. NOTE: all calls will be freed
153 */ 131 */
154int msi_kill ( MSISession *session ); 132int msi_kill ( MSISession *session );
155
156/** 133/**
157 * Callback setter. 134 * Callback setter.
158 */ 135 */
159void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata); 136void msi_register_callback(MSISession *session, msi_action_cb* callback, MSICallbackID id);
160
161/** 137/**
162 * Send invite request to friend_id. 138 * Send invite request to friend_number.
163 */ 139 */
164int msi_invite ( MSISession *session, 140int msi_invite ( MSISession* session, MSICall** call, uint32_t friend_number, uint8_t capabilities );
165 int32_t *call_index,
166 const MSICSettings *csettings,
167 uint32_t rngsec,
168 uint32_t friend_id );
169
170/** 141/**
171 * Hangup active call. 142 * Hangup call. NOTE: 'call' will be freed
172 */ 143 */
173int msi_hangup ( MSISession *session, int32_t call_index ); 144int msi_hangup ( MSICall* call );
174
175/** 145/**
176 * Answer active call request. 146 * Answer call request.
177 */ 147 */
178int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings ); 148int msi_answer ( MSICall* call, uint8_t capabilities );
179
180/**
181 * Cancel request.
182 */
183int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason );
184
185/**
186 * Reject incoming call.
187 */
188int msi_reject ( MSISession *session, int32_t call_index, const char *reason );
189
190/**
191 * Terminate the call.
192 */
193int msi_stopcall ( MSISession *session, int32_t call_index );
194
195/**
196 * Change codec settings of the current call.
197 */
198int msi_change_csettings ( MSISession *session, int32_t call_index, const MSICSettings *csettings );
199
200/** 149/**
201 * Main msi loop 150 * Change capabilities of the call.
202 */ 151 */
203void msi_do( MSISession *session ); 152int msi_change_capabilities ( MSICall* call, uint8_t capabilities );
204 153
205#endif /* __TOXMSI */ 154#endif /* MSI_H */
diff --git a/toxav/rtp.c b/toxav/rtp.c
index ccac7564..7b3a5ed0 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 *
@@ -25,19 +25,21 @@
25 25
26#include "../toxcore/logger.h" 26#include "../toxcore/logger.h"
27#include "../toxcore/util.h" 27#include "../toxcore/util.h"
28#include "../toxcore/Messenger.h"
28 29
29#include "rtp.h" 30#include "rtp.h"
30#include <stdlib.h> 31#include <stdlib.h>
31void queue_message(RTPSession *_session, RTPMessage *_msg); 32#include <assert.h>
32 33
33#define size_32 4 34#define size_32 4
35#define RTCP_REPORT_INTERVAL_MS 500
34 36
35#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0) 37#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) 38#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) 39#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) 40#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) 41#define ADD_SETTING_MARKER(_h, _v) do { ( _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) 42#define ADD_SETTING_PAYLOAD(_h, _v) do { ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0)
41 43
42#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6) 44#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6)
43#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5) 45#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5)
@@ -46,24 +48,260 @@ void queue_message(RTPSession *_session, RTPMessage *_msg);
46#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7) 48#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7)
47#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f) 49#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
48 50
49/** 51
50 * Checks if message came in late. 52typedef struct {
51 */ 53 uint64_t timestamp; /* in ms */
52static int check_late_message (RTPSession *session, RTPMessage *msg) 54
55 uint32_t received_packets;
56 uint32_t expected_packets;
57 /* ... other stuff in the future */
58} RTCPReport;
59
60typedef struct RTCPSession_s {
61 RTPSession *rtp_session;
62
63 uint8_t prefix;
64 uint64_t last_sent_report_ts;
65 uint32_t last_received_packets;
66 uint32_t last_expected_packets;
67
68 RingBuffer* pl_stats; /* Packet loss stats over time */
69} RTCPSession;
70
71
72RTPHeader *parse_header_in ( const uint8_t *payload, int length );
73RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length );
74uint8_t *parse_header_out ( const RTPHeader* header, uint8_t* payload );
75uint8_t *parse_ext_header_out ( const RTPExtHeader* header, uint8_t* payload );
76int handle_rtp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
77int handle_rtcp_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object );
78void send_rtcp_report ( RTCPSession* session, Messenger* m, uint32_t friendnumber );
79
80
81RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) )
53{ 82{
54 /* 83 assert(mcb);
55 * Check Sequence number. If this new msg has lesser number then the session->rsequnum 84 assert(cs);
56 * it shows that the message came in late. Also check timestamp to be 100% certain. 85 assert(m);
57 * 86
58 */ 87 RTPSession *retu = calloc(1, sizeof(RTPSession));
59 return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1; 88
89 if ( !retu ) {
90 LOGGER_WARNING("Alloc failed! Program might misbehave!");
91 return NULL;
92 }
93
94 retu->version = RTP_VERSION; /* It's always 2 */
95 retu->ssrc = random_int();
96 retu->payload_type = payload_type % 128;
97
98 retu->m = m;
99 retu->friend_number = friend_num;
100
101 if ( !(retu->csrc = calloc(1, sizeof(uint32_t))) ) {
102 LOGGER_WARNING("Alloc failed! Program might misbehave!");
103 free(retu);
104 return NULL;
105 }
106
107 retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */
108
109 /* Also set payload type as prefix */
110 retu->prefix = payload_type;
111
112 retu->cs = cs;
113 retu->mcb = mcb;
114
115 /* Initialize rtcp session */
116 if (!(retu->rtcp_session = calloc(1, sizeof(RTCPSession)))) {
117 LOGGER_WARNING("Alloc failed! Program might misbehave!");
118 free(retu->csrc);
119 free(retu);
120 return NULL;
121 }
122
123 retu->rtcp_session->prefix = payload_type + 2;
124 retu->rtcp_session->pl_stats = rb_new(4);
125 retu->rtcp_session->rtp_session = retu;
126
127 if (-1 == rtp_start_receiving(retu)) {
128 LOGGER_WARNING("Failed to start rtp receiving mode");
129 free(retu->rtcp_session);
130 free(retu->csrc);
131 free(retu);
132 return NULL;
133 }
134
135 return retu;
60} 136}
137void rtp_kill ( RTPSession *session )
138{
139 if ( !session ) return;
61 140
141 rtp_stop_receiving (session);
62 142
63/** 143 free ( session->ext_header );
64 * Extracts header from payload. 144 free ( session->csrc );
65 */ 145
66RTPHeader *extract_header ( const uint8_t *payload, int length ) 146 void* t;
147 while (!rb_empty(session->rtcp_session->pl_stats)) {
148 rb_read(session->rtcp_session->pl_stats, (void**) &t);
149 free(t);
150 }
151 rb_free(session->rtcp_session->pl_stats);
152
153 LOGGER_DEBUG("Terminated RTP session: %p", session);
154
155 /* And finally free session */
156 free ( session );
157}
158int rtp_do(RTPSession *session)
159{
160 if (!session || !session->rtcp_session)
161 return rtp_StateNormal;
162
163 if (current_time_monotonic() - session->rtcp_session->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) {
164 send_rtcp_report(session->rtcp_session, session->m, session->friend_number);
165 }
166
167 if (rb_full(session->rtcp_session->pl_stats)) {
168 RTCPReport* reports[4];
169
170 int i = 0;
171 for (; i < 4; i++)
172 rb_read(session->rtcp_session->pl_stats, (void**) reports + i);
173
174 /* Check for timed out reports (> 6 sec) */
175 uint64_t now = current_time_monotonic();
176 for (i = 0; i < 4 && (now - reports[i]->timestamp) < 6000; i ++);
177 for (; i < 4; i ++) {
178 rb_write(session->rtcp_session->pl_stats, reports[i]);
179 reports[i] = NULL;
180 }
181 if (!rb_empty(session->rtcp_session->pl_stats)) {
182 for (i = 0; reports[i] != NULL; i ++)
183 free(reports[i]);
184 return rtp_StateNormal; /* As some reports are timed out, we need more */
185 }
186
187 /* We have 4 on-time reports so we can proceed */
188 uint32_t quality = 100;
189 for (i = 0; i < 4; i++) {
190 uint32_t current = reports[i]->received_packets * 100 / reports[i]->expected_packets;
191 quality = MIN(quality, current);
192 free(reports[i]);
193 }
194
195 if (quality <= 90) {
196 LOGGER_WARNING("Stream quality: BAD (%d)", quality);
197 return rtp_StateBad;
198 } else if (quality >= 99) {
199 LOGGER_DEBUG("Stream quality: GOOD (%d)", quality);
200 return rtp_StateGood;
201 } else {
202 LOGGER_DEBUG("Stream quality: NORMAL (%d)", quality);
203 }
204 }
205 return rtp_StateNormal;
206}
207int rtp_start_receiving(RTPSession* session)
208{
209 if (session == NULL)
210 return -1;
211
212 if (m_callback_rtp_packet(session->m, session->friend_number, session->prefix,
213 handle_rtp_packet, session) == -1) {
214 LOGGER_WARNING("Failed to register rtp receive handler");
215 return -1;
216 }
217 if (m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix,
218 handle_rtcp_packet, session->rtcp_session) == -1) {
219 LOGGER_WARNING("Failed to register rtcp receive handler");
220 m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL);
221 return -1;
222 }
223
224 return 0;
225}
226int rtp_stop_receiving(RTPSession* session)
227{
228 if (session == NULL)
229 return -1;
230
231 m_callback_rtp_packet(session->m, session->friend_number, session->prefix, NULL, NULL);
232 m_callback_rtp_packet(session->m, session->friend_number, session->rtcp_session->prefix, NULL, NULL); /* RTCP */
233
234 return 0;
235}
236int rtp_send_data ( RTPSession *session, const uint8_t *data, uint16_t length, bool dummy )
237{
238 if ( !session ) {
239 LOGGER_WARNING("No session!");
240 return -1;
241 }
242
243 uint8_t parsed[MAX_RTP_SIZE];
244 uint8_t *it;
245
246 RTPHeader header[1];
247 memset(header, 0, sizeof(header));
248
249 ADD_FLAG_VERSION ( header, session->version );
250 ADD_FLAG_PADDING ( header, session->padding );
251 ADD_FLAG_EXTENSION ( header, session->extension );
252 ADD_FLAG_CSRCC ( header, session->cc );
253 ADD_SETTING_MARKER ( header, session->marker );
254
255 if (dummy)
256 ADD_SETTING_PAYLOAD ( header, (session->payload_type + 2) % 128 );
257 else
258 ADD_SETTING_PAYLOAD ( header, session->payload_type );
259
260 header->sequnum = session->sequnum;
261 header->timestamp = current_time_monotonic();
262 header->ssrc = session->ssrc;
263
264 int i;
265 for ( i = 0; i < session->cc; i++ )
266 header->csrc[i] = session->csrc[i];
267
268 header->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
269
270 uint32_t parsed_len = length + header->length + 1;
271 assert(parsed_len + (session->ext_header ? session->ext_header->length * size_32 : 0) < MAX_RTP_SIZE );
272
273 parsed[0] = session->prefix;
274 it = parse_header_out ( header, parsed + 1 );
275
276 if ( session->ext_header ) {
277 parsed_len += ( 4 /* Minimum ext header len */ + session->ext_header->length * size_32 );
278 it = parse_ext_header_out ( session->ext_header, it );
279 }
280
281 memcpy(it, data, length);
282
283 if ( -1 == send_custom_lossy_packet(session->m, session->friend_number, parsed, parsed_len) ) {
284 LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno));
285 return -1;
286 }
287
288 session->sequnum ++;
289 return 0;
290}
291void rtp_free_msg ( RTPMessage *msg )
292{
293 if ( msg->ext_header ) {
294 free ( msg->ext_header->table );
295 free ( msg->ext_header );
296 }
297
298 free ( msg->header );
299 free ( msg );
300}
301
302
303
304RTPHeader *parse_header_in ( const uint8_t *payload, int length )
67{ 305{
68 if ( !payload || !length ) { 306 if ( !payload || !length ) {
69 LOGGER_WARNING("No payload to extract!"); 307 LOGGER_WARNING("No payload to extract!");
@@ -84,12 +322,7 @@ RTPHeader *extract_header ( const uint8_t *payload, int length )
84 322
85 retu->flags = *it; 323 retu->flags = *it;
86 ++it; 324 ++it;
87 325
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 ) { 326 if ( GET_FLAG_VERSION(retu) != RTP_VERSION ) {
94 /* Deallocate */ 327 /* Deallocate */
95 LOGGER_WARNING("Invalid version!"); 328 LOGGER_WARNING("Invalid version!");
@@ -97,35 +330,28 @@ RTPHeader *extract_header ( const uint8_t *payload, int length )
97 return NULL; 330 return NULL;
98 } 331 }
99 332
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 ); 333 uint8_t cc = GET_FLAG_CSRCC ( retu );
105 int total = 12 /* Minimum header len */ + ( cc * 4 ); 334 int total = 12 /* Minimum header len */ + ( cc * 4 );
106 335
107 if ( length < total ) { 336 if ( length < total ) {
108 /* Deallocate */
109 LOGGER_WARNING("Length invalid!"); 337 LOGGER_WARNING("Length invalid!");
110 free(retu); 338 free(retu);
111 return NULL; 339 return NULL;
112 } 340 }
113 341
114 memset(retu->csrc, 0, 16 * sizeof (uint32_t));
115
116 retu->marker_payloadt = *it; 342 retu->marker_payloadt = *it;
117 ++it; 343 ++it;
118 retu->length = total; 344 retu->length = total;
119 345
120 346
121 memcpy(&retu->timestamp, it, sizeof(retu->timestamp)); 347 memcpy(&retu->timestamp, it, sizeof(retu->timestamp));
122 retu->timestamp = ntohl(retu->timestamp);
123 it += 4; 348 it += 4;
124 memcpy(&retu->ssrc, it, sizeof(retu->ssrc)); 349 memcpy(&retu->ssrc, it, sizeof(retu->ssrc));
350
351 retu->timestamp = ntohl(retu->timestamp);
125 retu->ssrc = ntohl(retu->ssrc); 352 retu->ssrc = ntohl(retu->ssrc);
126 353
127 uint8_t x; 354 uint8_t x;
128
129 for ( x = 0; x < cc; x++ ) { 355 for ( x = 0; x < cc; x++ ) {
130 it += 4; 356 it += 4;
131 memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x])); 357 memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x]));
@@ -134,11 +360,7 @@ RTPHeader *extract_header ( const uint8_t *payload, int length )
134 360
135 return retu; 361 return retu;
136} 362}
137 363RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length )
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{ 364{
143 const uint8_t *it = payload; 365 const uint8_t *it = payload;
144 366
@@ -149,44 +371,37 @@ RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length )
149 return NULL; 371 return NULL;
150 } 372 }
151 373
152 uint16_t ext_length; 374 memcpy(&retu->length, it, sizeof(retu->length));
153 memcpy(&ext_length, it, sizeof(ext_length)); 375 retu->length = ntohs(retu->length);
154 ext_length = ntohs(ext_length);
155 it += 2; 376 it += 2;
156 377
157 378 if ( length < ( retu->length * sizeof(uint32_t) ) ) {
158 if ( length < ( ext_length * sizeof(uint32_t) ) ) {
159 LOGGER_WARNING("Length invalid!"); 379 LOGGER_WARNING("Length invalid!");
160 free(retu); 380 free(retu);
161 return NULL; 381 return NULL;
162 } 382 }
163 383
164 retu->length = ext_length;
165 memcpy(&retu->type, it, sizeof(retu->type)); 384 memcpy(&retu->type, it, sizeof(retu->type));
166 retu->type = ntohs(retu->type); 385 retu->type = ntohs(retu->type);
386
167 it += 2; 387 it += 2;
168 388
169 if ( !(retu->table = calloc(ext_length, sizeof (uint32_t))) ) { 389 if ( !(retu->table = calloc(retu->length, sizeof (uint32_t))) ) {
170 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 390 LOGGER_WARNING("Alloc failed! Program might misbehave!");
171 free(retu); 391 free(retu);
172 return NULL; 392 return NULL;
173 } 393 }
174 394
175 uint16_t x; 395 uint16_t x;
176 396 for ( x = 0; x < retu->length; x++ ) {
177 for ( x = 0; x < ext_length; x++ ) {
178 it += 4; 397 it += 4;
179 memcpy(&(retu->table[x]), it, sizeof(retu->table[x])); 398 memcpy(retu->table + x, it, sizeof(*retu->table));
180 retu->table[x] = ntohl(retu->table[x]); 399 retu->table[x] = ntohl(retu->table[x]);
181 } 400 }
182 401
183 return retu; 402 return retu;
184} 403}
185 404uint8_t *parse_header_out ( const RTPHeader *header, uint8_t *payload )
186/**
187 * Adds header to payload. Make sure _payload_ has enough space.
188 */
189uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
190{ 405{
191 uint8_t cc = GET_FLAG_CSRCC ( header ); 406 uint8_t cc = GET_FLAG_CSRCC ( header );
192 uint8_t *it = payload; 407 uint8_t *it = payload;
@@ -206,7 +421,6 @@ uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
206 *it = header->marker_payloadt; 421 *it = header->marker_payloadt;
207 ++it; 422 ++it;
208 423
209
210 timestamp = htonl(header->timestamp); 424 timestamp = htonl(header->timestamp);
211 memcpy(it, &timestamp, sizeof(timestamp)); 425 memcpy(it, &timestamp, sizeof(timestamp));
212 it += 4; 426 it += 4;
@@ -223,11 +437,7 @@ uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
223 437
224 return it + 4; 438 return it + 4;
225} 439}
226 440uint8_t *parse_ext_header_out ( const RTPExtHeader *header, uint8_t *payload )
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{ 441{
232 uint8_t *it = payload; 442 uint8_t *it = payload;
233 uint16_t length; 443 uint16_t length;
@@ -242,9 +452,7 @@ uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
242 it -= 2; /* Return to 0 position */ 452 it -= 2; /* Return to 0 position */
243 453
244 if ( header->table ) { 454 if ( header->table ) {
245
246 uint16_t x; 455 uint16_t x;
247
248 for ( x = 0; x < header->length; x++ ) { 456 for ( x = 0; x < header->length; x++ ) {
249 it += 4; 457 it += 4;
250 entry = htonl(header->table[x]); 458 entry = htonl(header->table[x]);
@@ -254,275 +462,150 @@ uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
254 462
255 return it + 4; 463 return it + 4;
256} 464}
257 465int handle_rtp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
258/**
259 * Builds header from control session values.
260 */
261RTPHeader *build_header ( RTPSession *session )
262{ 466{
263 RTPHeader *retu = calloc ( 1, sizeof (RTPHeader) ); 467 (void) m;
468 (void) friendnumber;
469
470 RTPSession *session = object;
264 471
265 if ( !retu ) { 472 if ( !session || length < 13 || length > MAX_RTP_SIZE ) {
266 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 473 LOGGER_WARNING("No session or invalid length of received buffer!");
267 return NULL; 474 return -1;
268 } 475 }
476
477 RTPHeader* header = parse_header_in ( data + 1, length );
269 478
270 ADD_FLAG_VERSION ( retu, session->version ); 479 if ( !header ) {
271 ADD_FLAG_PADDING ( retu, session->padding ); 480 LOGGER_WARNING("Could not parse message: Header failed to extract!");
272 ADD_FLAG_EXTENSION ( retu, session->extension ); 481 return -1;
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
288 return retu;
289}
290
291
292/**
293 * Parses data into RTPMessage struct. Stores headers separately from the payload data
294 * and so the length variable is set accordingly.
295 */
296RTPMessage *msg_parse ( const uint8_t *data, int length )
297{
298 RTPMessage *retu = calloc(1, sizeof (RTPMessage));
299
300 retu->header = extract_header ( data, length ); /* It allocates memory and all */
301
302 if ( !retu->header ) {
303 LOGGER_WARNING("Header failed to extract!");
304 free(retu);
305 return NULL;
306 } 482 }
307 483
308 uint16_t from_pos = retu->header->length; 484 RTPExtHeader* ext_header = NULL;
309 retu->length = length - from_pos; 485
310 486 uint16_t from_pos = header->length + 1;
311 487 uint16_t msg_length = length - from_pos;
312 488
313 if ( GET_FLAG_EXTENSION ( retu->header ) ) { 489 if ( GET_FLAG_EXTENSION ( header ) ) {
314 retu->ext_header = extract_ext_header ( data + from_pos, length ); 490 ext_header = parse_ext_header_in ( data + from_pos, length );
315 491
316 if ( retu->ext_header ) { 492 if ( ext_header ) {
317 retu->length -= ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 ); 493 msg_length -= ( 4 /* Minimum ext header len */ + ext_header->length * size_32 );
318 from_pos += ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 ); 494 from_pos += ( 4 /* Minimum ext header len */ + ext_header->length * size_32 );
319 } else { /* Error */ 495 } else { /* Error */
320 LOGGER_WARNING("Ext Header failed to extract!"); 496 LOGGER_WARNING("Could not parse message: Ext Header failed to extract!");
321 rtp_free_msg(NULL, retu); 497 free(header);
322 return NULL; 498 return -1;
323 } 499 }
324 } else {
325 retu->ext_header = NULL;
326 }
327
328 if ( length - from_pos <= MAX_RTP_SIZE )
329 memcpy ( retu->data, data + from_pos, length - from_pos );
330 else {
331 LOGGER_WARNING("Invalid length!");
332 rtp_free_msg(NULL, retu);
333 return NULL;
334 } 500 }
335 501
336 retu->next = NULL; 502 if (msg_length > MAX_RTP_SIZE) {
337 503 LOGGER_WARNING("Could not parse message: Invalid length!");
338 return retu; 504 free(header);
339} 505 free(ext_header);
340
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{
346 RTPSession *session = object;
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; 506 return -1;
352 } 507 }
353 508
354 msg = msg_parse ( data + 1, length - 1 );
355
356 if ( !msg ) {
357 LOGGER_WARNING("Could not parse message!");
358 return -1;
359 }
360
361 /* Check if message came in late */ 509 /* Check if message came in late */
362 if ( check_late_message(session, msg) < 0 ) { /* Not late */ 510 if ( header->sequnum > session->rsequnum || header->timestamp > session->rtimestamp ) {
363 session->rsequnum = msg->header->sequnum; 511 /* Not late */
364 session->timestamp = msg->header->timestamp; 512 if (header->sequnum > session->rsequnum)
513 session->rtcp_session->last_expected_packets += header->sequnum - session->rsequnum;
514 else if (header->sequnum < session->rsequnum)
515 session->rtcp_session->last_expected_packets += (header->sequnum + 65535) - session->rsequnum;
516 else /* Usual case when transmission starts */
517 session->rtcp_session->last_expected_packets ++;
518
519 session->rsequnum = header->sequnum;
520 session->rtimestamp = header->timestamp;
365 } 521 }
366 522
367 queue_message(session, msg); 523 session->rtcp_session->last_received_packets ++;
368 524
369 return 0; 525 /* Check if the message is dummy. We don't keep dummy messages */
370} 526 if (GET_SETTING_PAYLOAD(header) == (session->payload_type + 2) % 128) {
371 527 LOGGER_DEBUG("Received dummy rtp message");
372/** 528 free(header);
373 * Allocate message and store data there 529 free(ext_header);
374 */ 530 return 0;
375RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length )
376{
377 if ( !session ) {
378 LOGGER_WARNING("No session!");
379 return NULL;
380 } 531 }
381 532
382 uint8_t *from_pos; 533 /* Otherwise we will store the message if we have an appropriate handler */
383 RTPMessage *retu = calloc(1, sizeof (RTPMessage)); 534 if (!session->mcb) {
384 535 LOGGER_DEBUG("No handler for the message of %d payload", GET_SETTING_PAYLOAD(header));
385 if ( !retu ) { 536 free(header);
386 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 537 free(ext_header);
387 return NULL; 538 return 0;
388 } 539 }
389 540
390 /* Sets header values and copies the extension header in retu */ 541 RTPMessage *msg = calloc(1, sizeof (RTPMessage) + msg_length);
391 retu->header = build_header ( session ); /* It allocates memory and all */ 542
392 retu->ext_header = session->ext_header; 543 if ( !msg ) {
393 544 LOGGER_WARNING("Could not parse message: Allocation failed!");
394 545 free(header);
395 uint32_t total_length = length + retu->header->length + 1; 546 free(ext_header);
396 547 return -1;
397 retu->data[0] = session->prefix;
398
399 if ( retu->ext_header ) {
400 total_length += ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 );
401
402 from_pos = add_header ( retu->header, retu->data + 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 } 548 }
407 549
408 /* 550 msg->header = header;
409 * Parses the extension header into the message 551 msg->ext_header = ext_header;
410 * Of course if any 552 msg->length = msg_length;
411 */ 553
412 554 memcpy ( msg->data, data + from_pos, msg_length );
413 /* Appends data on to retu->data */ 555
414 memcpy ( from_pos, data, length ); 556 return session->mcb (session->cs, msg);
415
416 retu->length = total_length;
417
418 retu->next = NULL;
419
420 return retu;
421} 557}
422 558int handle_rtcp_packet ( Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object )
423
424
425int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length )
426{ 559{
427 RTPMessage *msg = rtp_new_message (session, data, length); 560 (void) m;
428 561 (void) friendnumber;
429 if ( !msg ) return -1; 562
430 563 if (length < 9)
431 int ret = send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length); 564 return -1;
432 565
433 if ( 0 != ret) { 566 RTCPSession* session = object;
434 LOGGER_WARNING("Failed to send full packet (len: %d)! error: %i", length, ret); 567 RTCPReport* report = malloc(sizeof(RTCPReport));
435 rtp_free_msg ( session, msg ); 568
436 return rtp_ErrorSending; 569 memcpy(&report->received_packets, data + 1, 4);
570 memcpy(&report->expected_packets, data + 5, 4);
571
572 report->received_packets = ntohl(report->received_packets);
573 report->expected_packets = ntohl(report->expected_packets);
574
575 if (report->expected_packets == 0 || report->received_packets > report->expected_packets) {
576 LOGGER_WARNING("Malformed rtcp report! %d %d", report->expected_packets, report->received_packets);
577 free(report);
578 return 0;
437 } 579 }
438 580
439 /* Set sequ number */ 581 report->timestamp = current_time_monotonic();
440 session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1; 582
441 rtp_free_msg ( session, msg ); 583 free(rb_write(session->pl_stats, report));
442 584
585 LOGGER_DEBUG("Got rtcp report: ex: %d rc: %d", report->expected_packets, report->received_packets);
443 return 0; 586 return 0;
444} 587}
445 588void send_rtcp_report(RTCPSession* session, Messenger* m, uint32_t friendnumber)
446void rtp_free_msg ( RTPSession *session, RTPMessage *msg )
447{
448 if ( !session ) {
449 if ( msg->ext_header ) {
450 free ( msg->ext_header->table );
451 free ( msg->ext_header );
452 }
453 } else {
454 if ( msg->ext_header && session->ext_header != msg->ext_header ) {
455 free ( msg->ext_header->table );
456 free ( msg->ext_header );
457 }
458 }
459
460 free ( msg->header );
461 free ( msg );
462}
463
464RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
465{ 589{
466 RTPSession *retu = calloc(1, sizeof(RTPSession)); 590 if (session->last_expected_packets == 0)
467 591 return;
468 if ( !retu ) { 592
469 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 593 uint8_t parsed[9];
470 return NULL; 594 parsed[0] = session->prefix;
471 } 595
472 596 uint32_t received_packets = htonl(session->last_received_packets);
473 if ( -1 == m_callback_rtp_packet(messenger, friend_num, payload_type, rtp_handle_packet, retu)) { 597 uint32_t expected_packets = htonl(session->last_expected_packets);
474 LOGGER_ERROR("Error setting custom register handler for rtp session"); 598
475 free(retu); 599 memcpy(parsed + 1, &received_packets, 4);
476 return NULL; 600 memcpy(parsed + 5, &expected_packets, 4);
477 } 601
478 602 if (-1 == send_custom_lossy_packet(m, friendnumber, parsed, sizeof(parsed)))
479 LOGGER_DEBUG("Registered packet handler: pt: %d; fid: %d", payload_type, friend_num); 603 LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", sizeof(parsed), strerror(errno));
480 604 else {
481 retu->version = RTP_VERSION; /* It's always 2 */ 605 LOGGER_DEBUG("Sent rtcp report: ex: %d rc: %d", session->last_expected_packets, session->last_received_packets);
482 retu->padding = 0; /* If some additional data is needed about the packet */ 606
483 retu->extension = 0; /* If extension to header is needed */ 607 session->last_received_packets = 0;
484 retu->cc = 1; /* Amount of contributors */ 608 session->last_expected_packets = 0;
485 retu->csrc = NULL; /* Container */ 609 session->last_sent_report_ts = current_time_monotonic();
486 retu->ssrc = random_int();
487 retu->marker = 0;
488 retu->payload_type = payload_type % 128;
489
490 retu->dest = friend_num;
491
492 retu->rsequnum = retu->sequnum = 0;
493
494 retu->ext_header = NULL; /* When needed allocate */
495
496
497 if ( !(retu->csrc = calloc(1, sizeof (uint32_t))) ) {
498 LOGGER_WARNING("Alloc failed! Program might misbehave!");
499 free(retu);
500 return NULL;
501 } 610 }
502 611} \ No newline at end of file
503 retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */
504
505 /* Also set payload type as prefix */
506 retu->prefix = payload_type;
507
508 /*
509 *
510 */
511 return retu;
512}
513
514void rtp_kill ( RTPSession *session, Messenger *messenger )
515{
516 if ( !session ) return;
517
518 m_callback_rtp_packet(messenger, session->dest, session->prefix, NULL, NULL);
519
520 free ( session->ext_header );
521 free ( session->csrc );
522
523 LOGGER_DEBUG("Terminated RTP session: %p", session);
524
525 /* And finally free session */
526 free ( session );
527
528}
diff --git a/toxav/rtp.h b/toxav/rtp.h
index c98840ac..b18d6f8d 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,25 +19,45 @@
19 * 19 *
20 */ 20 */
21 21
22#ifndef __TOXRTP 22#ifndef RTP_H
23#define __TOXRTP 23#define RTP_H
24 24
25#define RTP_VERSION 2 25#define RTP_VERSION 2
26#include <inttypes.h>
27// #include <pthread.h>
28 26
29#include "../toxcore/Messenger.h" 27#include "../toxcore/Messenger.h"
30 28
31#define MAX_SEQU_NUM 65535 29#define LOGGED_LOCK(mutex) do { \
32#define MAX_RTP_SIZE 65535 30 /*LOGGER_DEBUG("Locking mutex: %p", mutex);*/\
31 pthread_mutex_lock(mutex);\
32 /*LOGGER_DEBUG("Locked mutex: %p", mutex);*/\
33} while(0)
34
35#define LOGGED_UNLOCK(mutex) do { \
36 /*LOGGER_DEBUG("Unlocking mutex: %p", mutex);*/\
37 pthread_mutex_unlock(mutex);\
38 /*LOGGER_DEBUG("Unlocked mutex: %p", mutex);*/\
39} while(0)
40
41#define MAX_RTP_SIZE 1500
33 42
34typedef enum {
35 rtp_ErrorSending = -40
36} RTPError;
37/** 43/**
38 * Standard rtp header 44 * Payload type identifier. Also used as rtp callback prefix. (Not dummies)
45 */
46enum {
47 rtp_TypeAudio = 192,
48 rtp_TypeVideo,
49};
50
51enum {
52 rtp_StateBad = -1,
53 rtp_StateNormal,
54 rtp_StateGood,
55};
56
57/**
58 * Standard rtp header.
39 */ 59 */
40typedef struct _RTPHeader { 60typedef struct {
41 uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */ 61 uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
42 uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */ 62 uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */
43 uint16_t sequnum; /* Sequence Number */ 63 uint16_t sequnum; /* Sequence Number */
@@ -45,83 +65,89 @@ typedef struct _RTPHeader {
45 uint32_t ssrc; /* SSRC */ 65 uint32_t ssrc; /* SSRC */
46 uint32_t csrc[16]; /* CSRC's table */ 66 uint32_t csrc[16]; /* CSRC's table */
47 uint32_t length; /* Length of the header in payload string. */ 67 uint32_t length; /* Length of the header in payload string. */
48
49} RTPHeader; 68} RTPHeader;
50 69/**
51/**
52 * Standard rtp extension header. 70 * Standard rtp extension header.
53 */ 71 */
54typedef struct _RTPExtHeader { 72typedef struct {
55 uint16_t type; /* Extension profile */ 73 uint16_t type; /* Extension profile */
56 uint16_t length; /* Number of extensions */ 74 uint16_t length; /* Number of extensions */
57 uint32_t *table; /* Extension's table */ 75 uint32_t *table; /* Extension's table */
58
59} RTPExtHeader; 76} RTPExtHeader;
60 77
61/** 78/**
62 * Standard rtp message. 79 * Standard rtp message.
63 */ 80 */
64typedef struct _RTPMessage { 81typedef struct RTPMessage_s {
65 RTPHeader *header; 82 RTPHeader *header;
66 RTPExtHeader *ext_header; 83 RTPExtHeader *ext_header;
67 84
68 uint8_t data[MAX_RTP_SIZE];
69 uint32_t length; 85 uint32_t length;
70 86 uint8_t data[];
71 struct _RTPMessage *next;
72} RTPMessage; 87} RTPMessage;
73 88
74/** 89/**
75 * RTP control session. 90 * RTP control session.
76 */ 91 */
77typedef struct _RTPSession { 92typedef struct {
78 uint8_t version; 93 uint8_t version;
79 uint8_t padding; 94 uint8_t padding;
80 uint8_t extension; 95 uint8_t extension;
81 uint8_t cc; 96 uint8_t cc;
82 uint8_t marker; 97 uint8_t marker;
83 uint8_t payload_type; 98 uint8_t payload_type;
84 uint16_t sequnum; /* Set when sending */ 99 uint16_t sequnum; /* Sending sequence number */
85 uint16_t rsequnum; /* Check when recving msg */ 100 uint16_t rsequnum; /* Receiving sequence number */
86 uint32_t timestamp; 101 uint32_t rtimestamp;
87 uint32_t ssrc; 102 uint32_t ssrc;
88 uint32_t *csrc; 103 uint32_t *csrc;
89 104
90 /* If some additional data must be sent via message 105 /* If some additional data must be sent via message
91 * apply it here. Only by allocating this member you will be 106 * apply it here. Only by allocating this member you will be
92 * automatically placing it within a message. 107 * automatically placing it within a message.
93 */ 108 */
94 RTPExtHeader *ext_header; 109 RTPExtHeader *ext_header;
95 110
96 /* Msg prefix for core to know when recving */ 111 /* Msg prefix for core to know when recving */
97 uint8_t prefix; 112 uint8_t prefix;
98
99 int dest;
100 113
101 struct _CSSession *cs; 114 Messenger *m;
115 int friend_number;
116 struct RTCPSession_s *rtcp_session;
102 117
118 void *cs;
119 int (*mcb) (void*, RTPMessage* msg);
120
103} RTPSession; 121} RTPSession;
104 122
105/** 123/**
106 * Must be called before calling any other rtp function. 124 * Must be called before calling any other rtp function.
107 */ 125 */
108RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ); 126RTPSession *rtp_new ( int payload_type, Messenger *m, int friend_num, void* cs, int (*mcb) (void*, RTPMessage*) );
109
110/** 127/**
111 * Terminate the session. 128 * Terminate the session.
112 */ 129 */
113void rtp_kill ( RTPSession *session, Messenger *messenger ); 130void rtp_kill ( RTPSession* session );
114
115/** 131/**
116 * Sends msg to _RTPSession::dest 132 * Do periodical rtp work.
117 */ 133 */
118int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ); 134int rtp_do(RTPSession *session);
119 135/**
136 * By default rtp is in receiving state
137 */
138int rtp_start_receiving (RTPSession *session);
139/**
140 * Pause rtp receiving mode.
141 */
142int rtp_stop_receiving (RTPSession *session);
143/**
144 * Sends msg to RTPSession::dest
145 */
146int rtp_send_data ( RTPSession* session, const uint8_t* data, uint16_t length, bool dummy );
120/** 147/**
121 * Dealloc msg. 148 * Dealloc msg.
122 */ 149 */
123void rtp_free_msg ( RTPSession *session, RTPMessage *msg ); 150void rtp_free_msg ( RTPMessage *msg );
124
125 151
126 152
127#endif /* __TOXRTP */ 153#endif /* RTP_H */
diff --git a/toxav/toxav.c b/toxav/toxav.c
index a51ec5e3..35523319 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,1325 @@ 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#define BITRATE_CHANGE_TESTING_TIME_MS 4000
45/* true if invalid call index */ 39
46#define CALL_INVALID_INDEX(idx, max) (idx < 0 || idx >= max) 40typedef struct ToxAvBitrateAdapter_s {
47 41 bool active;
48const ToxAvCSettings av_DefaultSettings = { 42 uint64_t end_time;
49 av_TypeAudio, 43 uint64_t next_send;
50 44 uint64_t next_send_interval;
51 500, 45 uint32_t bit_rate;
52 1280, 46} ToxAvBitrateAdapter;
53 720, 47
54 48typedef struct ToxAVCall_s {
55 32000, 49 ToxAV* av;
56 20, 50
57 48000, 51 pthread_mutex_t mutex_audio[1];
58 1 52 PAIR(RTPSession *, ACSession *) audio;
59}; 53
60 54 pthread_mutex_t mutex_video[1];
61static const uint32_t jbuf_capacity = 6; 55 PAIR(RTPSession *, VCSession *) video;
62static const uint8_t audio_index = 0, video_index = 1; 56
63
64typedef struct _ToxAvCall {
65 pthread_mutex_t mutex[1]; 57 pthread_mutex_t mutex[1];
66 pthread_mutex_t mutex_encoding_audio[1]; 58
67 pthread_mutex_t mutex_encoding_video[1]; 59 bool active;
68 pthread_mutex_t mutex_do[1]; 60 MSICall* msi_call;
69 RTPSession *crtps[2]; /** Audio is first and video is second */ 61 uint32_t friend_number;
70 CSSession *cs; 62
71 _Bool active; 63 uint32_t audio_bit_rate; /* Sending audio bit rate */
72} ToxAvCall; 64 uint32_t video_bit_rate; /* Sending video bit rate */
73 65
74struct _ToxAv { 66 ToxAvBitrateAdapter aba;
75 Messenger *messenger; 67 ToxAvBitrateAdapter vba;
76 MSISession *msi_session; /** Main msi session */ 68
77 ToxAvCall *calls; /** Per-call params */ 69 /** Required for monitoring changes in states */
78 uint32_t max_calls; 70 uint8_t previous_self_capabilities;
79 71
80 PAIR(ToxAvAudioCallback, void *) acb; 72 /** Quality control */
81 PAIR(ToxAvVideoCallback, void *) vcb; 73 uint64_t time_audio_good;
82 74 uint32_t last_bad_audio_bit_rate;
83 /* Decode time measure */ 75 uint64_t time_video_good;
84 int32_t dectmsscount; /** Measure count */ 76 uint32_t last_bad_video_bit_rate;
85 int32_t dectmsstotal; /** Last cycle total */ 77
86 int32_t avgdectms; /** Average decoding time in ms */ 78 struct ToxAVCall_s *prev;
79 struct ToxAVCall_s *next;
80} ToxAVCall;
81
82struct ToxAV {
83 Messenger* m;
84 MSISession* msi;
85
86 /* Two-way storage: first is array of calls and second is list of calls with head and tail */
87 ToxAVCall** calls;
88 uint32_t calls_tail;
89 uint32_t calls_head;
90 pthread_mutex_t mutex[1];
91
92 PAIR(toxav_call_cb *, void*) ccb; /* Call callback */
93 PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */
94 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
95 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
96 PAIR(toxav_audio_bit_rate_status_cb *, void *) abcb; /* Audio bit rate control callback */
97 PAIR(toxav_video_bit_rate_status_cb *, void *) vbcb; /* Video bit rate control callback */
98
99 /** Decode time measures */
100 int32_t dmssc; /** Measure count */
101 int32_t dmsst; /** Last cycle total */
102 int32_t dmssa; /** Average decoding time in ms */
103
104 uint32_t interval; /** Calculated interval */
87}; 105};
88 106
89static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from) 107
108int callback_invite(void* toxav_inst, MSICall* call);
109int callback_start(void* toxav_inst, MSICall* call);
110int callback_end(void* toxav_inst, MSICall* call);
111int callback_error(void* toxav_inst, MSICall* call);
112int callback_capabilites(void* toxav_inst, MSICall* call);
113
114bool audio_bit_rate_invalid(uint32_t bit_rate);
115bool video_bit_rate_invalid(uint32_t bit_rate);
116bool invoke_call_state(ToxAV* av, uint32_t friend_number, uint32_t state);
117ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error);
118ToxAVCall* call_get(ToxAV* av, uint32_t friend_number);
119ToxAVCall* call_remove(ToxAVCall* call);
120bool call_prepare_transmission(ToxAVCall* call);
121void call_kill_transmission(ToxAVCall* call);
122void ba_set(ToxAvBitrateAdapter* ba, uint32_t bit_rate);
123bool ba_shoud_send_dummy(ToxAvBitrateAdapter* ba);
124
125uint32_t toxav_version_major(void)
90{ 126{
91 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); 127 return 0;
92 return (const MSICSettings *) from;
93} 128}
94 129uint32_t toxav_version_minor(void)
95static const ToxAvCSettings *toxavcsettings_cast (const MSICSettings *from)
96{ 130{
97 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); 131 return 0;
98 return (const ToxAvCSettings *) from;
99
100} 132}
101 133uint32_t toxav_version_patch(void)
102ToxAv *toxav_new( Tox *messenger, int32_t max_calls)
103{ 134{
104 ToxAv *av = calloc ( sizeof(ToxAv), 1); 135 return 0;
136}
137bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch)
138{
139 (void)major;
140 (void)minor;
141 (void)patch;
142
143 return 1;
144}
105 145
146ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error)
147{
148 TOXAV_ERR_NEW rc = TOXAV_ERR_NEW_OK;
149 ToxAV *av = NULL;
150
151 if (tox == NULL) {
152 rc = TOXAV_ERR_NEW_NULL;
153 goto END;
154 }
155
156 if (((Messenger*)tox)->msi_packet) {
157 rc = TOXAV_ERR_NEW_MULTIPLE;
158 goto END;
159 }
160
161 av = calloc (sizeof(ToxAV), 1);
162
106 if (av == NULL) { 163 if (av == NULL) {
107 LOGGER_WARNING("Allocation failed!"); 164 LOGGER_WARNING("Allocation failed!");
108 return NULL; 165 rc = TOXAV_ERR_NEW_MALLOC;
166 goto END;
109 } 167 }
110 168
111 av->messenger = (Messenger *)messenger; 169 if (create_recursive_mutex(av->mutex) != 0) {
112 av->msi_session = msi_new(av->messenger, max_calls); 170 LOGGER_WARNING("Mutex creation failed!");
113 av->msi_session->agent_handler = av; 171 rc = TOXAV_ERR_NEW_MALLOC;
114 av->calls = calloc(sizeof(ToxAvCall), max_calls); 172 goto END;
115 av->max_calls = max_calls;
116
117 unsigned int i;
118
119 for (i = 0; i < max_calls; ++i) {
120 if (create_recursive_mutex(av->calls[i].mutex) != 0 ) {
121 LOGGER_WARNING("Failed to init call(%u) mutex!", i);
122 msi_kill(av->msi_session);
123
124 free(av->calls);
125 free(av);
126 return NULL;
127 }
128 } 173 }
129 174
175 av->m = (Messenger *)tox;
176 av->msi = msi_new(av->m);
177
178 if (av->msi == NULL) {
179 pthread_mutex_destroy(av->mutex);
180 rc = TOXAV_ERR_NEW_MALLOC;
181 goto END;
182 }
183
184 av->interval = 200;
185 av->msi->av = av;
186
187 msi_register_callback(av->msi, callback_invite, msi_OnInvite);
188 msi_register_callback(av->msi, callback_start, msi_OnStart);
189 msi_register_callback(av->msi, callback_end, msi_OnEnd);
190 msi_register_callback(av->msi, callback_error, msi_OnError);
191 msi_register_callback(av->msi, callback_error, msi_OnPeerTimeout);
192 msi_register_callback(av->msi, callback_capabilites, msi_OnCapabilities);
193
194END:
195 if (error)
196 *error = rc;
197
198 if (rc != TOXAV_ERR_NEW_OK) {
199 free(av);
200 av = NULL;
201 }
202
130 return av; 203 return av;
131} 204}
132 205
133void toxav_kill ( ToxAv *av ) 206void toxav_kill(ToxAV* av)
134{ 207{
135 uint32_t i; 208 if (av == NULL)
136 209 return;
137 for (i = 0; i < av->max_calls; i ++) { 210 pthread_mutex_lock(av->mutex);
138 if ( av->calls[i].crtps[audio_index] ) 211
139 rtp_kill(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle); 212 msi_kill(av->msi);
140 213
141 214 /* Msi kill will hang up all calls so just clean these calls */
142 if ( av->calls[i].crtps[video_index] ) 215 if (av->calls) {
143 rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle); 216 ToxAVCall* it = call_get(av, av->calls_head);
144 217 while (it) {
145 if ( av->calls[i].cs ) 218 call_kill_transmission(it);
146 cs_kill(av->calls[i].cs); 219 it = call_remove(it); /* This will eventually free av->calls */
147 220 }
148 pthread_mutex_destroy(av->calls[i].mutex);
149 } 221 }
150 222
151 msi_kill(av->msi_session); 223 pthread_mutex_unlock(av->mutex);
152 224 pthread_mutex_destroy(av->mutex);
153 free(av->calls);
154 free(av); 225 free(av);
155} 226}
156 227
157uint32_t toxav_do_interval(ToxAv *av) 228Tox* toxav_get_tox(const ToxAV* av)
158{ 229{
159 int i = 0; 230 return (Tox*) av->m;
160 uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */
161
162 for (; i < av->max_calls; i ++) {
163 pthread_mutex_lock(av->calls[i].mutex);
164
165 if (av->calls[i].active) {
166 /* This should work. Video payload will always come in greater intervals */
167 rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc);
168 }
169
170 pthread_mutex_unlock(av->calls[i].mutex);
171 }
172
173 return rc < av->avgdectms ? 0 : rc - av->avgdectms;
174} 231}
175 232
176void toxav_do(ToxAv *av) 233uint32_t toxav_iteration_interval(const ToxAV* av)
177{ 234{
178 msi_do(av->msi_session); 235 /* If no call is active interval is 200 */
236 return av->calls ? av->interval : 200;
237}
179 238
239void toxav_iterate(ToxAV* av)
240{
241 pthread_mutex_lock(av->mutex);
242 if (av->calls == NULL) {
243 pthread_mutex_unlock(av->mutex);
244 return;
245 }
246
180 uint64_t start = current_time_monotonic(); 247 uint64_t start = current_time_monotonic();
181 248 int32_t rc = 500;
182 uint32_t i = 0; 249
183 250 ToxAVCall* i = av->calls[av->calls_head];
184 for (; i < av->max_calls; i ++) { 251 for (; i; i = i->next) {
185 pthread_mutex_lock(av->calls[i].mutex); 252 if (i->active) {
186 253 pthread_mutex_lock(i->mutex);
187 if (av->calls[i].active) { 254 pthread_mutex_unlock(av->mutex);
188 pthread_mutex_lock(av->calls[i].mutex_do); 255
189 pthread_mutex_unlock(av->calls[i].mutex); 256 ac_do(i->audio.second);
190 cs_do(av->calls[i].cs); 257 if (rtp_do(i->audio.first) < 0) {
191 pthread_mutex_unlock(av->calls[i].mutex_do); 258 /* Bad transmission */
192 } else { 259
193 pthread_mutex_unlock(av->calls[i].mutex); 260 uint32_t bb = i->audio_bit_rate;
261
262 if (i->aba.active) {
263 bb = i->aba.bit_rate;
264 /* Stop sending dummy packets */
265 memset(&i->aba, 0, sizeof(i->aba));
266 }
267
268 /* Notify app */
269 if (av->abcb.first)
270 av->abcb.first (av, i->friend_number, false, bb, av->abcb.second);
271 } else if (i->aba.active && i->aba.end_time < current_time_monotonic()) {
272
273 i->audio_bit_rate = i->aba.bit_rate;
274
275 /* Notify user about the new bit rate */
276 if (av->abcb.first)
277 av->abcb.first (av, i->friend_number, true, i->aba.bit_rate, av->abcb.second);
278
279 /* Stop sending dummy packets */
280 memset(&i->aba, 0, sizeof(i->aba));
281 }
282
283 vc_do(i->video.second);
284 if (rtp_do(i->video.first) < 0) {
285 /* Bad transmission */
286 uint32_t bb = i->video_bit_rate;
287
288 if (i->vba.active) {
289 bb = i->vba.bit_rate;
290 /* Stop sending dummy packets */
291 memset(&i->vba, 0, sizeof(i->vba));
292 }
293
294 /* Notify app */
295 if (av->vbcb.first)
296 av->vbcb.first (av, i->friend_number, false, bb, av->vbcb.second);
297
298 } else if (i->vba.active && i->vba.end_time < current_time_monotonic()) {
299
300 i->video_bit_rate = i->vba.bit_rate;
301
302 /* Notify user about the new bit rate */
303 if (av->vbcb.first)
304 av->vbcb.first (av, i->friend_number, true, i->vba.bit_rate, av->vbcb.second);
305
306 /* Stop sending dummy packets */
307 memset(&i->vba, 0, sizeof(i->vba));
308 }
309
310 if (i->msi_call->self_capabilities & msi_CapRAudio &&
311 i->msi_call->peer_capabilities & msi_CapSAudio)
312 rc = MIN(i->audio.second->last_packet_frame_duration, rc);
313
314 if (i->msi_call->self_capabilities & msi_CapRVideo &&
315 i->msi_call->peer_capabilities & msi_CapSVideo)
316 rc = MIN(i->video.second->lcfd, (uint32_t) rc);
317
318 uint32_t fid = i->friend_number;
319
320 pthread_mutex_unlock(i->mutex);
321 pthread_mutex_lock(av->mutex);
322
323 /* In case this call is popped from container stop iteration */
324 if (call_get(av, fid) != i)
325 break;
194 } 326 }
195 } 327 }
196 328 pthread_mutex_unlock(av->mutex);
197 uint64_t end = current_time_monotonic(); 329
198 330 av->interval = rc < av->dmssa ? 0 : (rc - av->dmssa);
199 /* TODO maybe use variable for sizes */ 331 av->dmsst += current_time_monotonic() - start;
200 av->dectmsstotal += end - start; 332
201 333 if (++av->dmssc == 3) {
202 if (++av->dectmsscount == 3) { 334 av->dmssa = av->dmsst / 3 + 2 /* NOTE Magic Offset for precission */;
203 av->avgdectms = av->dectmsstotal / 3 + 2 /* NOTE Magic Offset */; 335 av->dmssc = 0;
204 av->dectmsscount = 0; 336 av->dmsst = 0;
205 av->dectmsstotal = 0;
206 } 337 }
207} 338}
208 339
209void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata ) 340bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error)
210{
211 msi_register_callback(av->msi_session, (MSICallbackType)cb, (MSICallbackID) id, userdata);
212}
213
214void toxav_register_audio_callback(ToxAv *av, ToxAvAudioCallback cb, void *userdata)
215{
216 av->acb.first = cb;
217 av->acb.second = userdata;
218}
219
220void toxav_register_video_callback(ToxAv *av, ToxAvVideoCallback cb, void *userdata)
221{
222 av->vcb.first = cb;
223 av->vcb.second = userdata;
224}
225
226int toxav_call (ToxAv *av,
227 int32_t *call_index,
228 int user,
229 const ToxAvCSettings *csettings,
230 int ringing_seconds )
231{
232 return msi_invite(av->msi_session, call_index, msicsettings_cast(csettings), ringing_seconds * 1000, user);
233}
234
235int toxav_hangup ( ToxAv *av, int32_t call_index )
236{ 341{
237 return msi_hangup(av->msi_session, call_index); 342 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate))
343 ||(video_bit_rate && video_bit_rate_invalid(video_bit_rate))
344 ) {
345 if (error)
346 *error = TOXAV_ERR_CALL_INVALID_BIT_RATE;
347 return false;
348 }
349
350 pthread_mutex_lock(av->mutex);
351 ToxAVCall* call = call_new(av, friend_number, error);
352 if (call == NULL) {
353 pthread_mutex_unlock(av->mutex);
354 return false;
355 }
356
357 call->audio_bit_rate = audio_bit_rate;
358 call->video_bit_rate = video_bit_rate;
359
360 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
361
362 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
363 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
364
365 if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) {
366 call_remove(call);
367 if (error)
368 *error = TOXAV_ERR_CALL_MALLOC;
369 pthread_mutex_unlock(av->mutex);
370 return false;
371 }
372
373 call->msi_call->av_call = call;
374 pthread_mutex_unlock(av->mutex);
375
376 return true;
238} 377}
239 378
240int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) 379void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data)
241{ 380{
242 return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings)); 381 pthread_mutex_lock(av->mutex);
382 av->ccb.first = function;
383 av->ccb.second = user_data;
384 pthread_mutex_unlock(av->mutex);
243} 385}
244 386
245int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) 387bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error)
246{ 388{
247 return msi_reject(av->msi_session, call_index, reason); 389 pthread_mutex_lock(av->mutex);
390
391 TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK;
392 if (m_friend_exists(av->m, friend_number) == 0) {
393 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND;
394 goto END;
395 }
396
397 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate))
398 ||(video_bit_rate && video_bit_rate_invalid(video_bit_rate))
399 ) {
400 rc = TOXAV_ERR_ANSWER_INVALID_BIT_RATE;
401 goto END;
402 }
403
404 ToxAVCall* call = call_get(av, friend_number);
405 if (call == NULL) {
406 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING;
407 goto END;
408 }
409
410 if (!call_prepare_transmission(call)) {
411 rc = TOXAV_ERR_ANSWER_CODEC_INITIALIZATION;
412 goto END;
413 }
414
415 call->audio_bit_rate = audio_bit_rate;
416 call->video_bit_rate = video_bit_rate;
417
418 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
419
420 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
421 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
422
423 if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0)
424 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; /* the only reason for msi_answer to fail */
425
426
427END:
428 pthread_mutex_unlock(av->mutex);
429
430 if (error)
431 *error = rc;
432
433 return rc == TOXAV_ERR_ANSWER_OK;
248} 434}
249 435
250int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) 436void toxav_callback_call_state(ToxAV* av, toxav_call_state_cb* function, void* user_data)
251{ 437{
252 return msi_cancel(av->msi_session, call_index, peer_id, reason); 438 pthread_mutex_lock(av->mutex);
439 av->scb.first = function;
440 av->scb.second = user_data;
441 pthread_mutex_unlock(av->mutex);
253} 442}
254 443
255int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) 444bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error)
256{ 445{
257 return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); 446 pthread_mutex_lock(av->mutex);
447 TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK;
448
449 if (m_friend_exists(av->m, friend_number) == 0) {
450 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND;
451 goto END;
452 }
453
454 ToxAVCall* call = call_get(av, friend_number);
455 if (call == NULL || (!call->active && control != TOXAV_CALL_CONTROL_CANCEL)) {
456 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
457 goto END;
458 }
459
460 switch (control) {
461 case TOXAV_CALL_CONTROL_RESUME: {
462 /* Only act if paused and had media transfer active before */
463 if (call->msi_call->self_capabilities == 0 &&
464 call->previous_self_capabilities ) {
465
466 if (msi_change_capabilities(call->msi_call,
467 call->previous_self_capabilities) == -1) {
468 /* The only reason for this function to fail is invalid state
469 * ( not active ) */
470 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
471 goto END;
472 }
473
474 rtp_start_receiving(call->audio.first);
475 rtp_start_receiving(call->video.first);
476 } else {
477 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
478 goto END;
479 }
480 } break;
481
482 case TOXAV_CALL_CONTROL_PAUSE: {
483 /* Only act if not already paused */
484 if (call->msi_call->self_capabilities) {
485 call->previous_self_capabilities = call->msi_call->self_capabilities;
486
487 if (msi_change_capabilities(call->msi_call, 0) == -1 ) {
488 /* The only reason for this function to fail is invalid state
489 * ( not active ) */
490 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
491 goto END;
492 }
493
494 rtp_stop_receiving(call->audio.first);
495 rtp_stop_receiving(call->video.first);
496 } else {
497 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
498 goto END;
499 }
500 } break;
501
502 case TOXAV_CALL_CONTROL_CANCEL: {
503 /* Hang up */
504 msi_hangup(call->msi_call);
505
506 /* No mather the case, terminate the call */
507 call_kill_transmission(call);
508 call_remove(call);
509 } break;
510
511 case TOXAV_CALL_CONTROL_MUTE_AUDIO: {
512 if (call->msi_call->self_capabilities & msi_CapRAudio) {
513 if (msi_change_capabilities(call->msi_call, call->
514 msi_call->self_capabilities ^ msi_CapRAudio) == -1) {
515 /* The only reason for this function to fail is invalid state
516 * ( not active ) */
517 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
518 goto END;
519 }
520
521 rtp_stop_receiving(call->audio.first);
522 } else {
523 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
524 goto END;
525 }
526 } break;
527
528 case TOXAV_CALL_CONTROL_UNMUTE_AUDIO: {
529 if (call->msi_call->self_capabilities ^ msi_CapRAudio) {
530 if (msi_change_capabilities(call->msi_call, call->
531 msi_call->self_capabilities | msi_CapRAudio) == -1) {
532 /* The only reason for this function to fail is invalid state
533 * ( not active ) */
534 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
535 goto END;
536 }
537
538 rtp_start_receiving(call->audio.first);
539 } else {
540 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
541 goto END;
542 }
543 } break;
544
545 case TOXAV_CALL_CONTROL_HIDE_VIDEO: {
546 if (call->msi_call->self_capabilities & msi_CapRVideo) {
547 if (msi_change_capabilities(call->msi_call, call->
548 msi_call->self_capabilities ^ msi_CapRVideo) == -1) {
549 /* The only reason for this function to fail is invalid state
550 * ( not active ) */
551 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
552 goto END;
553 }
554
555 rtp_stop_receiving(call->video.first);
556 } else {
557 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
558 goto END;
559 }
560 } break;
561
562 case TOXAV_CALL_CONTROL_SHOW_VIDEO: {
563 if (call->msi_call->self_capabilities ^ msi_CapRVideo) {
564 if (msi_change_capabilities(call->msi_call, call->
565 msi_call->self_capabilities | msi_CapRVideo) == -1) {
566 /* The only reason for this function to fail is invalid state
567 * ( not active ) */
568 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
569 goto END;
570 }
571
572 rtp_start_receiving(call->audio.first);
573 } else {
574 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
575 goto END;
576 }
577 } break;
578 }
579
580END:
581 pthread_mutex_unlock(av->mutex);
582
583 if (error)
584 *error = rc;
585
586 return rc == TOXAV_ERR_CALL_CONTROL_OK;
258} 587}
259 588
260int toxav_stop_call ( ToxAv *av, int32_t call_index ) 589void toxav_callback_audio_bit_rate_status(ToxAV* av, toxav_audio_bit_rate_status_cb* function, void* user_data)
261{ 590{
262 return msi_stopcall(av->msi_session, call_index); 591 pthread_mutex_lock(av->mutex);
592 av->abcb.first = function;
593 av->abcb.second = user_data;
594 pthread_mutex_unlock(av->mutex);
263} 595}
264 596
265int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_video ) 597bool toxav_audio_bit_rate_set(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE* error)
266{ 598{
267 if ( !av->msi_session || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || 599 TOXAV_ERR_SET_BIT_RATE rc = TOXAV_ERR_SET_BIT_RATE_OK;
268 !av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer) { 600 ToxAVCall* call;
269 LOGGER_ERROR("Error while starting RTP session: invalid call!\n"); 601
270 return av_ErrorNoCall; 602 if (m_friend_exists(av->m, friend_number) == 0) {
603 rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_FOUND;
604 goto END;
271 } 605 }
272 606
273 ToxAvCall *call = &av->calls[call_index]; 607 if (audio_bit_rate_invalid(audio_bit_rate)) {
274 608 rc = TOXAV_ERR_SET_BIT_RATE_INVALID;
275 pthread_mutex_lock(call->mutex); 609 goto END;
276
277 if (call->active) {
278 pthread_mutex_unlock(call->mutex);
279 LOGGER_ERROR("Error while starting RTP session: call already active!\n");
280 return av_ErrorAlreadyInCallWithPeer;
281 }
282
283 if (pthread_mutex_init(call->mutex_encoding_audio, NULL) != 0
284 || pthread_mutex_init(call->mutex_encoding_video, NULL) != 0 || pthread_mutex_init(call->mutex_do, NULL) != 0) {
285 pthread_mutex_unlock(call->mutex);
286 LOGGER_ERROR("Error while starting RTP session: mutex initializing failed!\n");
287 return av_ErrorUnknown;
288 } 610 }
289 611
290 const ToxAvCSettings *c_peer = toxavcsettings_cast 612 pthread_mutex_lock(av->mutex);
291 (&av->msi_session->calls[call_index]->csettings_peer[0]); 613 call = call_get(av, friend_number);
292 const ToxAvCSettings *c_self = toxavcsettings_cast 614 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
293 (&av->msi_session->calls[call_index]->csettings_local); 615 pthread_mutex_unlock(av->mutex);
294 616 rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_IN_CALL;
295 LOGGER_DEBUG( 617 goto END;
296 "Type: %u(s) %u(p)\n"
297 "Video bitrate: %u(s) %u(p)\n"
298 "Video height: %u(s) %u(p)\n"
299 "Video width: %u(s) %u(p)\n"
300 "Audio bitrate: %u(s) %u(p)\n"
301 "Audio framedur: %u(s) %u(p)\n"
302 "Audio sample rate: %u(s) %u(p)\n"
303 "Audio channels: %u(s) %u(p)\n",
304 c_self->call_type, c_peer->call_type,
305 c_self->video_bitrate, c_peer->video_bitrate,
306 c_self->max_video_height, c_peer->max_video_height,
307 c_self->max_video_width, c_peer->max_video_width,
308 c_self->audio_bitrate, c_peer->audio_bitrate,
309 c_self->audio_frame_duration, c_peer->audio_frame_duration,
310 c_self->audio_sample_rate, c_peer->audio_sample_rate,
311 c_self->audio_channels, c_peer->audio_channels );
312
313 if ( !(call->cs = cs_new(c_self, c_peer, jbuf_capacity, support_video)) ) {
314 LOGGER_ERROR("Error while starting Codec State!\n");
315 pthread_mutex_unlock(call->mutex);
316 return av_ErrorInitializingCodecs;
317 } 618 }
318 619
319 call->cs->agent = av; 620 if (call->audio_bit_rate == audio_bit_rate || (call->aba.active && call->aba.bit_rate == audio_bit_rate)) {
320 call->cs->call_idx = call_index; 621 pthread_mutex_unlock(av->mutex);
321 622 goto END;
322 call->cs->acb.first = av->acb.first;
323 call->cs->acb.second = av->acb.second;
324
325 call->cs->vcb.first = av->vcb.first;
326 call->cs->vcb.second = av->vcb.second;
327
328
329 call->crtps[audio_index] =
330 rtp_new(msi_TypeAudio, av->messenger, av->msi_session->calls[call_index]->peers[0]);
331
332 if ( !call->crtps[audio_index] ) {
333 LOGGER_ERROR("Error while starting audio RTP session!\n");
334 goto error;
335 } 623 }
336 624
337 call->crtps[audio_index]->cs = call->cs; 625
338 626 pthread_mutex_lock(call->mutex);
339 if ( support_video ) { 627
340 call->crtps[video_index] = 628 if (audio_bit_rate > call->audio_bit_rate && !force)
341 rtp_new(msi_TypeVideo, av->messenger, av->msi_session->calls[call_index]->peers[0]); 629 ba_set(&call->aba, audio_bit_rate);
342 630 else {
343 if ( !call->crtps[video_index] ) { 631 /* Cancel any previous non forceful bitrate change request */
344 LOGGER_ERROR("Error while starting video RTP session!\n"); 632 memset(&call->aba, 0, sizeof(call->aba));
345 goto error; 633 call->audio_bit_rate = audio_bit_rate;
346 } 634
347 635 if (!force && av->abcb.first)
348 call->crtps[video_index]->cs = call->cs; 636 av->abcb.first (av, call->friend_number, true, audio_bit_rate, av->abcb.second);
349 } 637 }
350 638
351 call->active = 1;
352 pthread_mutex_unlock(call->mutex); 639 pthread_mutex_unlock(call->mutex);
353 return av_ErrorNone; 640 pthread_mutex_unlock(av->mutex);
354error: 641
355 rtp_kill(call->crtps[audio_index], av->messenger); 642END:
356 call->crtps[audio_index] = NULL; 643 if (error)
357 rtp_kill(call->crtps[video_index], av->messenger); 644 *error = rc;
358 call->crtps[video_index] = NULL; 645
359 cs_kill(call->cs); 646 return rc == TOXAV_ERR_SET_BIT_RATE_OK;
360 call->cs = NULL; 647}
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 648
366 pthread_mutex_unlock(call->mutex); 649void toxav_callback_video_bit_rate_status(ToxAV* av, toxav_video_bit_rate_status_cb* function, void* user_data)
367 return av_ErrorCreatingRtpSessions; 650{
651 pthread_mutex_lock(av->mutex);
652 av->vbcb.first = function;
653 av->vbcb.second = user_data;
654 pthread_mutex_unlock(av->mutex);
368} 655}
369 656
370int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) 657bool toxav_video_bit_rate_set(ToxAV* av, uint32_t friend_number, uint32_t video_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE* error)
371{ 658{
372 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 659 TOXAV_ERR_SET_BIT_RATE rc = TOXAV_ERR_SET_BIT_RATE_OK;
373 LOGGER_WARNING("Invalid call index: %d", call_index); 660 ToxAVCall* call;
374 return av_ErrorNoCall; 661
662 if (m_friend_exists(av->m, friend_number) == 0) {
663 rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_FOUND;
664 goto END;
375 } 665 }
376 666
377 ToxAvCall *call = &av->calls[call_index]; 667 if (video_bit_rate_invalid(video_bit_rate)) {
378 668 rc = TOXAV_ERR_SET_BIT_RATE_INVALID;
669 goto END;
670 }
671
672 pthread_mutex_lock(av->mutex);
673 call = call_get(av, friend_number);
674 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
675 pthread_mutex_unlock(av->mutex);
676 rc = TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_IN_CALL;
677 goto END;
678 }
679
680 if (call->video_bit_rate == video_bit_rate || (call->vba.active && call->vba.bit_rate == video_bit_rate)) {
681 pthread_mutex_unlock(av->mutex);
682 goto END;
683 }
684
379 pthread_mutex_lock(call->mutex); 685 pthread_mutex_lock(call->mutex);
380 686
381 if (!call->active) { 687 if (video_bit_rate > call->video_bit_rate && !force)
382 pthread_mutex_unlock(call->mutex); 688 ba_set(&call->vba, video_bit_rate);
383 LOGGER_WARNING("Action on inactive call: %d", call_index); 689 else {
384 return av_ErrorInvalidState; 690 /* Cancel any previous non forceful bitrate change request */
691 memset(&call->vba, 0, sizeof(call->vba));
692 call->video_bit_rate = video_bit_rate;
693
694 if (!force && av->vbcb.first)
695 av->vbcb.first (av, call->friend_number, true, video_bit_rate, av->vbcb.second);
385 } 696 }
386 697
387 call->active = 0;
388
389 pthread_mutex_lock(call->mutex_encoding_audio);
390 pthread_mutex_unlock(call->mutex_encoding_audio);
391 pthread_mutex_lock(call->mutex_encoding_video);
392 pthread_mutex_unlock(call->mutex_encoding_video);
393 pthread_mutex_lock(call->mutex_do);
394 pthread_mutex_unlock(call->mutex_do);
395
396 rtp_kill(call->crtps[audio_index], av->messenger);
397 call->crtps[audio_index] = NULL;
398 rtp_kill(call->crtps[video_index], av->messenger);
399 call->crtps[video_index] = NULL;
400 cs_kill(call->cs);
401 call->cs = NULL;
402
403 pthread_mutex_destroy(call->mutex_encoding_audio);
404 pthread_mutex_destroy(call->mutex_encoding_video);
405 pthread_mutex_destroy(call->mutex_do);
406
407 pthread_mutex_unlock(call->mutex); 698 pthread_mutex_unlock(call->mutex);
408 699 pthread_mutex_unlock(av->mutex);
409 return av_ErrorNone; 700
701END:
702 if (error)
703 *error = rc;
704
705 return rc == TOXAV_ERR_SET_BIT_RATE_OK;
410} 706}
411 707
412static int toxav_send_rtp_payload(ToxAv *av, 708bool toxav_audio_send_frame(ToxAV* av, uint32_t friend_number, const int16_t* pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME* error)
413 ToxAvCall *call,
414 ToxAvCallType type,
415 const uint8_t *payload,
416 unsigned int length)
417{ 709{
418 if (call->crtps[type - av_TypeAudio]) { 710 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
419 711 ToxAVCall* call;
420 /* Audio */ 712
421 if (type == av_TypeAudio) 713 if (m_friend_exists(av->m, friend_number) == 0) {
422 return rtp_send_msg(call->crtps[audio_index], av->messenger, payload, length); 714 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
423 715 goto END;
424 /* Video */ 716 }
425 int parts = cs_split_video_payload(call->cs, payload, length); 717
426 718 pthread_mutex_lock(av->mutex);
427 if (parts < 0) return parts; 719 call = call_get(av, friend_number);
428 720 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
429 uint16_t part_size; 721 pthread_mutex_unlock(av->mutex);
430 const uint8_t *iter; 722 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
431 723 goto END;
432 int i; 724 }
433 725
434 for (i = 0; i < parts; i++) { 726 pthread_mutex_lock(call->mutex_audio);
435 iter = cs_get_split_video_frame(call->cs, &part_size); 727 pthread_mutex_unlock(av->mutex);
436 728
437 if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) < 0) 729 if ( pcm == NULL ) {
438 return av_ErrorSendingPayload; 730 pthread_mutex_unlock(call->mutex_audio);
731 rc = TOXAV_ERR_SEND_FRAME_NULL;
732 goto END;
733 }
734
735 if ( channels > 2 ) {
736 pthread_mutex_unlock(call->mutex_audio);
737 rc = TOXAV_ERR_SEND_FRAME_INVALID;
738 goto END;
739 }
740
741 { /* Encode and send */
742 if (ac_reconfigure_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) {
743 pthread_mutex_unlock(call->mutex_audio);
744 rc = TOXAV_ERR_SEND_FRAME_INVALID;
745 goto END;
439 } 746 }
440 747
441 return av_ErrorNone; 748 uint8_t dest[sample_count + sizeof(sampling_rate)]; /* This is more than enough always */
442 749
443 } else return av_ErrorNoRtpSession; 750 sampling_rate = htonl(sampling_rate);
751 memcpy(dest, &sampling_rate, sizeof(sampling_rate));
752 int vrc = opus_encode(call->audio.second->encoder, pcm, sample_count,
753 dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate));
754
755 if (vrc < 0) {
756 LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc));
757 pthread_mutex_unlock(call->mutex_audio);
758 rc = TOXAV_ERR_SEND_FRAME_INVALID;
759 goto END;
760 }
761
762 if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate), false) != 0) {
763 LOGGER_WARNING("Failed to send audio packet");
764 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
765 }
766
767
768 /* For bit rate measurement; send dummy packet */
769 if (ba_shoud_send_dummy(&call->aba)) {
770 sampling_rate = ntohl(sampling_rate);
771 if (ac_reconfigure_test_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) {
772 /* FIXME should the bit rate changing fail here? */
773 pthread_mutex_unlock(call->mutex_audio);
774 rc = TOXAV_ERR_SEND_FRAME_INVALID;
775 goto END;
776 }
777
778 sampling_rate = htonl(sampling_rate);
779 memcpy(dest, &sampling_rate, sizeof(sampling_rate));
780 vrc = opus_encode(call->audio.second->test_encoder, pcm, sample_count,
781 dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate));
782
783 if (vrc < 0) {
784 LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc));
785 pthread_mutex_unlock(call->mutex_audio);
786 rc = TOXAV_ERR_SEND_FRAME_INVALID;
787 goto END;
788 }
789
790 if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate), true) != 0) {
791 LOGGER_WARNING("Failed to send audio packet");
792 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
793 }
794
795 if (call->aba.end_time == (uint64_t) ~0)
796 call->aba.end_time = current_time_monotonic() + BITRATE_CHANGE_TESTING_TIME_MS;
797 }
798 }
799
800
801 pthread_mutex_unlock(call->mutex_audio);
802
803END:
804 if (error)
805 *error = rc;
806
807 return rc == TOXAV_ERR_SEND_FRAME_OK;
444} 808}
445 809
446int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) 810bool toxav_video_send_frame(ToxAV* av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, TOXAV_ERR_SEND_FRAME* error)
447{ 811{
448 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 812 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
449 LOGGER_WARNING("Invalid call index: %d", call_index); 813 ToxAVCall* call;
450 return av_ErrorNoCall; 814
815 if (m_friend_exists(av->m, friend_number) == 0) {
816 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
817 goto END;
451 } 818 }
452 819
453 820 pthread_mutex_lock(av->mutex);
454 ToxAvCall *call = &av->calls[call_index]; 821 call = call_get(av, friend_number);
455 pthread_mutex_lock(call->mutex); 822 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
456 823 pthread_mutex_unlock(av->mutex);
457 if (!call->active) { 824 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
458 pthread_mutex_unlock(call->mutex); 825 goto END;
459 LOGGER_WARNING("Action on inactive call: %d", call_index);
460 return av_ErrorInvalidState;
461 } 826 }
462 827
463 if (!(call->cs->capabilities & cs_VideoEncoding)) { 828 pthread_mutex_lock(call->mutex_video);
464 pthread_mutex_unlock(call->mutex); 829 pthread_mutex_unlock(av->mutex);
465 LOGGER_WARNING("Call doesn't support encoding video: %d", call_index); 830
466 return av_ErrorInvalidState; 831 if ( y == NULL || u == NULL || v == NULL ) {
832 pthread_mutex_unlock(call->mutex_video);
833 rc = TOXAV_ERR_SEND_FRAME_NULL;
834 goto END;
467 } 835 }
468 836
469 if (cs_set_video_encoder_resolution(call->cs, input->w, input->h) < 0) { 837 if ( vc_reconfigure_encoder(call->video.second, call->video_bit_rate * 1000, width, height) != 0 ) {
470 pthread_mutex_unlock(call->mutex); 838 pthread_mutex_unlock(call->mutex_video);
471 return av_ErrorSettingVideoResolution; 839 rc = TOXAV_ERR_SEND_FRAME_INVALID;
840 goto END;
472 } 841 }
473 842
474 pthread_mutex_lock(call->mutex_encoding_video); 843 { /* Encode */
475 pthread_mutex_unlock(call->mutex); 844 vpx_image_t img;
476 845 img.w = img.h = img.d_w = img.d_h = 0;
477 int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); 846 vpx_img_alloc(&img, VPX_IMG_FMT_VPXI420, width, height, 1);
478 847
479 if ( rc != VPX_CODEC_OK) { 848 /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes."
480 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); 849 * http://fourcc.org/yuv.php#IYUV
481 pthread_mutex_unlock(call->mutex_encoding_video); 850 */
482 return av_ErrorEncodingVideo; 851 memcpy(img.planes[VPX_PLANE_Y], y, width * height);
852 memcpy(img.planes[VPX_PLANE_U], u, (width/2) * (height/2));
853 memcpy(img.planes[VPX_PLANE_V], v, (width/2) * (height/2));
854
855 int vrc = vpx_codec_encode(call->video.second->encoder, &img,
856 call->video.second->frame_counter, 1, 0, MAX_ENCODE_TIME_US);
857
858 vpx_img_free(&img);
859 if ( vrc != VPX_CODEC_OK) {
860 pthread_mutex_unlock(call->mutex_video);
861 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc));
862 rc = TOXAV_ERR_SEND_FRAME_INVALID;
863 goto END;
864 }
483 } 865 }
484 866
485 ++call->cs->frame_counter; 867 ++call->video.second->frame_counter;
486 868
487 vpx_codec_iter_t iter = NULL; 869 { /* Split and send */
488 const vpx_codec_cx_pkt_t *pkt; 870 vpx_codec_iter_t iter = NULL;
489 int copied = 0; 871 const vpx_codec_cx_pkt_t *pkt;
490 872
491 while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { 873 vc_init_video_splitter_cycle(call->video.second);
492 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { 874
493 if ( copied + pkt->data.frame.sz > dest_max ) { 875 while ( (pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter)) ) {
494 pthread_mutex_unlock(call->mutex_encoding_video); 876 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
495 return av_ErrorPacketTooLarge; 877 int parts = vc_update_video_splitter_cycle(call->video.second, pkt->data.frame.buf,
878 pkt->data.frame.sz);
879
880 if (parts < 0) /* Should never happen though */
881 continue;
882
883 uint16_t part_size;
884 const uint8_t *iter;
885
886 int i;
887 for (i = 0; i < parts; i++) {
888 iter = vc_iterate_split_video_frame(call->video.second, &part_size);
889
890 if (rtp_send_data(call->video.first, iter, part_size, false) < 0) {
891 pthread_mutex_unlock(call->mutex_video);
892 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
893 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
894 goto END;
895 }
896 }
496 } 897 }
497
498 memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz);
499 copied += pkt->data.frame.sz;
500 } 898 }
501 } 899 }
502 900
503 pthread_mutex_unlock(call->mutex_encoding_video); 901 if (ba_shoud_send_dummy(&call->vba)) {
504 return copied; 902 if ( vc_reconfigure_test_encoder(call->video.second, call->vba.bit_rate * 1000, width, height) != 0 ) {
903 pthread_mutex_unlock(call->mutex_video);
904 rc = TOXAV_ERR_SEND_FRAME_INVALID;
905 goto END;
906 }
907
908 /* FIXME use the same image as before */
909 vpx_image_t img;
910 img.w = img.h = img.d_w = img.d_h = 0;
911 vpx_img_alloc(&img, VPX_IMG_FMT_VPXI420, width, height, 1);
912
913 /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes."
914 * http://fourcc.org/yuv.php#IYUV
915 */
916 memcpy(img.planes[VPX_PLANE_Y], y, width * height);
917 memcpy(img.planes[VPX_PLANE_U], u, (width/2) * (height/2));
918 memcpy(img.planes[VPX_PLANE_V], v, (width/2) * (height/2));
919
920 int vrc = vpx_codec_encode(call->video.second->test_encoder, &img,
921 call->video.second->test_frame_counter, 1, 0, MAX_ENCODE_TIME_US);
922
923 vpx_img_free(&img);
924 if ( vrc != VPX_CODEC_OK) {
925 pthread_mutex_unlock(call->mutex_video);
926 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc));
927 rc = TOXAV_ERR_SEND_FRAME_INVALID;
928 goto END;
929 }
930
931 call->video.second->test_frame_counter++;
932
933 vpx_codec_iter_t iter = NULL;
934 const vpx_codec_cx_pkt_t *pkt;
935
936 /* Send the encoded data as dummy packets */
937 while ( (pkt = vpx_codec_get_cx_data(call->video.second->test_encoder, &iter)) ) {
938 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
939
940 int parts = pkt->data.frame.sz / 1300;
941 int i;
942 for (i = 0; i < parts; i++) {
943 if (rtp_send_data(call->video.first, pkt->data.frame.buf + i * 1300, 1300, true) < 0) {
944 pthread_mutex_unlock(call->mutex_video);
945 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
946 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
947 goto END;
948 }
949 }
950
951 if (pkt->data.frame.sz % 1300) {
952 if (rtp_send_data(call->video.first, pkt->data.frame.buf + parts * 1300, pkt->data.frame.sz % 1300, true) < 0) {
953 pthread_mutex_unlock(call->mutex_video);
954 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
955 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
956 goto END;
957 }
958 }
959 }
960 }
961
962 if (call->vba.end_time == (uint64_t) ~0)
963 call->vba.end_time = current_time_monotonic() + BITRATE_CHANGE_TESTING_TIME_MS;
964 }
965
966 pthread_mutex_unlock(call->mutex_video);
967
968END:
969 if (error)
970 *error = rc;
971
972 return rc == TOXAV_ERR_SEND_FRAME_OK;
505} 973}
506 974
507int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) 975void toxav_callback_audio_receive_frame(ToxAV* av, toxav_audio_receive_frame_cb* function, void* user_data)
508{ 976{
509 977 pthread_mutex_lock(av->mutex);
510 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 978 av->acb.first = function;
511 LOGGER_WARNING("Invalid call index: %d", call_index); 979 av->acb.second = user_data;
512 return av_ErrorNoCall; 980 pthread_mutex_unlock(av->mutex);
513 }
514
515 ToxAvCall *call = &av->calls[call_index];
516 pthread_mutex_lock(call->mutex);
517
518
519 if (!call->active) {
520 pthread_mutex_unlock(call->mutex);
521 LOGGER_WARNING("Action on inactive call: %d", call_index);
522 return av_ErrorInvalidState;
523 }
524
525 int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size);
526 pthread_mutex_unlock(call->mutex);
527
528 return rc;
529} 981}
530 982
531int toxav_prepare_audio_frame ( ToxAv *av, 983void toxav_callback_video_receive_frame(ToxAV* av, toxav_video_receive_frame_cb* function, void* user_data)
532 int32_t call_index,
533 uint8_t *dest,
534 int dest_max,
535 const int16_t *frame,
536 int frame_size)
537{ 984{
538 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 985 pthread_mutex_lock(av->mutex);
539 LOGGER_WARNING("Action on nonexisting call: %d", call_index); 986 av->vcb.first = function;
540 return av_ErrorNoCall; 987 av->vcb.second = user_data;
541 } 988 pthread_mutex_unlock(av->mutex);
989}
542 990
543 ToxAvCall *call = &av->calls[call_index];
544 pthread_mutex_lock(call->mutex);
545 991
546 if (!call->active) { 992/*******************************************************************************
547 pthread_mutex_unlock(call->mutex); 993 *
548 LOGGER_WARNING("Action on inactive call: %d", call_index); 994 * :: Internal
549 return av_ErrorInvalidState; 995 *
996 ******************************************************************************/
997int callback_invite(void* toxav_inst, MSICall* call)
998{
999 ToxAV* toxav = toxav_inst;
1000 pthread_mutex_lock(toxav->mutex);
1001
1002 ToxAVCall* av_call = call_new(toxav, call->friend_number, NULL);
1003 if (av_call == NULL) {
1004 LOGGER_WARNING("Failed to initialize call...");
1005 pthread_mutex_unlock(toxav->mutex);
1006 return -1;
550 } 1007 }
551 1008
552 pthread_mutex_lock(call->mutex_encoding_audio); 1009 call->av_call = av_call;
553 pthread_mutex_unlock(call->mutex); 1010 av_call->msi_call = call;
554 int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); 1011
555 pthread_mutex_unlock(call->mutex_encoding_audio); 1012 if (toxav->ccb.first)
556 1013 toxav->ccb.first(toxav, call->friend_number, call->peer_capabilities & msi_CapSAudio,
557 if (rc < 0) { 1014 call->peer_capabilities & msi_CapSVideo, toxav->ccb.second);
558 LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); 1015 else {
559 return av_ErrorEncodingAudio; 1016 /* No handler to capture the call request, send failure */
1017 pthread_mutex_unlock(toxav->mutex);
1018 return -1;
560 } 1019 }
561 1020
562 return rc; 1021 pthread_mutex_unlock(toxav->mutex);
1022 return 0;
563} 1023}
564 1024
565int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size) 1025int callback_start(void* toxav_inst, MSICall* call)
566{ 1026{
567 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 1027 ToxAV* toxav = toxav_inst;
568 LOGGER_WARNING("Action on nonexisting call: %d", call_index); 1028 pthread_mutex_lock(toxav->mutex);
569 return av_ErrorNoCall; 1029
1030 ToxAVCall* av_call = call_get(toxav, call->friend_number);
1031
1032 if (av_call == NULL) {
1033 /* Should this ever happen? */
1034 pthread_mutex_unlock(toxav->mutex);
1035 return -1;
570 } 1036 }
571 1037
572 ToxAvCall *call = &av->calls[call_index]; 1038 if (!call_prepare_transmission(av_call)) {
573 pthread_mutex_lock(call->mutex); 1039 callback_error(toxav_inst, call);
574 1040 pthread_mutex_unlock(toxav->mutex);
575 1041 return -1;
576 if (!call->active) {
577 pthread_mutex_unlock(call->mutex);
578 LOGGER_WARNING("Action on inactive call: %d", call_index);
579 return av_ErrorInvalidState;
580 } 1042 }
581 1043
582 int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size); 1044 if (!invoke_call_state(toxav, call->friend_number, call->peer_capabilities)) {
583 pthread_mutex_unlock(call->mutex); 1045 callback_error(toxav_inst, call);
584 return rc; 1046 pthread_mutex_unlock(toxav->mutex);
1047 return -1;
1048 }
1049
1050 pthread_mutex_unlock(toxav->mutex);
1051 return 0;
585} 1052}
586 1053
587int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ) 1054int callback_end(void* toxav_inst, MSICall* call)
588{ 1055{
589 if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || 1056 ToxAV* toxav = toxav_inst;
590 !av->msi_session->calls[call_index] || av->msi_session->calls[call_index]->peer_count <= peer ) 1057 pthread_mutex_lock(toxav->mutex);
591 return av_ErrorNoCall; 1058
592 1059 invoke_call_state(toxav, call->friend_number, TOXAV_CALL_STATE_FINISHED);
593 *dest = *toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]); 1060
594 return av_ErrorNone; 1061 call_kill_transmission(call->av_call);
1062 call_remove(call->av_call);
1063
1064 pthread_mutex_unlock(toxav->mutex);
1065 return 0;
595} 1066}
596 1067
597int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) 1068int callback_error(void* toxav_inst, MSICall* call)
598{ 1069{
599 if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] 1070 ToxAV* toxav = toxav_inst;
600 || av->msi_session->calls[call_index]->peer_count <= peer ) 1071 pthread_mutex_lock(toxav->mutex);
601 return av_ErrorNoCall; 1072
602 1073 invoke_call_state(toxav, call->friend_number, TOXAV_CALL_STATE_ERROR);
603 return av->msi_session->calls[call_index]->peers[peer]; 1074
1075 call_kill_transmission(call->av_call);
1076 call_remove(call->av_call);
1077
1078 pthread_mutex_unlock(toxav->mutex);
1079 return 0;
604} 1080}
605 1081
606ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) 1082int callback_capabilites(void* toxav_inst, MSICall* call)
607{ 1083{
608 if ( CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] ) 1084 ToxAV* toxav = toxav_inst;
609 return av_CallNonExistent; 1085 pthread_mutex_lock(toxav->mutex);
610 1086
611 return av->msi_session->calls[call_index]->state; 1087 invoke_call_state(toxav, call->friend_number, call->peer_capabilities);
612 1088
1089 pthread_mutex_unlock(toxav->mutex);
1090 return 0;
613} 1091}
614 1092
615int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) 1093bool audio_bit_rate_invalid(uint32_t bit_rate)
616{ 1094{
617 return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (CSCapabilities) capability : 0; 1095 /* Opus RFC 6716 section-2.1.1 dictates the following:
618 /* 0 is error here */ 1096 * Opus supports all bit rates from 6 kbit/s to 510 kbit/s.
1097 */
1098 return bit_rate < 6 || bit_rate > 510;
619} 1099}
620 1100
621Tox *toxav_get_tox(ToxAv *av) 1101bool video_bit_rate_invalid(uint32_t bit_rate)
622{ 1102{
623 return (Tox *)av->messenger; 1103 (void) bit_rate;
1104 /* TODO: If anyone knows the answer to this one please fill it up */
1105 return false;
624} 1106}
625 1107
626int toxav_get_active_count(ToxAv *av) 1108bool invoke_call_state(ToxAV* av, uint32_t friend_number, uint32_t state)
627{ 1109{
628 if (!av) return -1; 1110 if (av->scb.first)
629 1111 av->scb.first(av, friend_number, state, av->scb.second);
630 int rc = 0, i = 0; 1112 else
631 1113 return false;
632 for (; i < av->max_calls; i++) { 1114 return true;
633 pthread_mutex_lock(av->calls[i].mutex); 1115}
634
635 if (av->calls[i].active) rc++;
636 1116
637 pthread_mutex_unlock(av->calls[i].mutex); 1117ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
1118{
1119 /* Assumes mutex locked */
1120 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
1121 ToxAVCall* call = NULL;
1122
1123 if (m_friend_exists(av->m, friend_number) == 0) {
1124 rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND;
1125 goto END;
1126 }
1127
1128 if (m_get_friend_connectionstatus(av->m, friend_number) < 1) {
1129 rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
1130 goto END;
1131 }
1132
1133 if (call_get(av, friend_number) != NULL) {
1134 rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL;
1135 goto END;
638 } 1136 }
1137
1138
1139 call = calloc(sizeof(ToxAVCall), 1);
1140
1141 if (call == NULL) {
1142 rc = TOXAV_ERR_CALL_MALLOC;
1143 goto END;
1144 }
1145
1146 call->av = av;
1147 call->friend_number = friend_number;
1148
1149 if (av->calls == NULL) { /* Creating */
1150 av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1);
1151
1152 if (av->calls == NULL) {
1153 free(call);
1154 call = NULL;
1155 rc = TOXAV_ERR_CALL_MALLOC;
1156 goto END;
1157 }
1158
1159 av->calls_tail = av->calls_head = friend_number;
1160
1161 } else if (av->calls_tail < friend_number) { /* Appending */
1162 void* tmp = realloc(av->calls, sizeof(ToxAVCall*) * friend_number + 1);
1163
1164 if (tmp == NULL) {
1165 free(call);
1166 call = NULL;
1167 rc = TOXAV_ERR_CALL_MALLOC;
1168 goto END;
1169 }
1170
1171 av->calls = tmp;
1172
1173 /* Set fields in between to null */
1174 uint32_t i = av->calls_tail + 1;
1175 for (; i < friend_number; i ++)
1176 av->calls[i] = NULL;
1177
1178 call->prev = av->calls[av->calls_tail];
1179 av->calls[av->calls_tail]->next = call;
1180
1181 av->calls_tail = friend_number;
1182
1183 } else if (av->calls_head > friend_number) { /* Inserting at front */
1184 call->next = av->calls[av->calls_head];
1185 av->calls[av->calls_head]->prev = call;
1186 av->calls_head = friend_number;
1187 }
1188
1189 av->calls[friend_number] = call;
1190
1191END:
1192 if (error)
1193 *error = rc;
1194
1195 return call;
1196}
639 1197
640 return rc; 1198ToxAVCall* call_get(ToxAV* av, uint32_t friend_number)
1199{
1200 /* Assumes mutex locked */
1201 if (av->calls == NULL || av->calls_tail < friend_number)
1202 return NULL;
1203
1204 return av->calls[friend_number];
641} 1205}
642 1206
643/* Create a new toxav group. 1207ToxAVCall* call_remove(ToxAVCall* call)
644 *
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{ 1208{
656 Messenger *m = tox; 1209 if (call == NULL)
657 return add_av_groupchat(m->group_chat_object, audio_callback, userdata); 1210 return NULL;
1211
1212 uint32_t friend_number = call->friend_number;
1213 ToxAV* av = call->av;
1214
1215 ToxAVCall* prev = call->prev;
1216 ToxAVCall* next = call->next;
1217
1218 free(call);
1219
1220 if (prev)
1221 prev->next = next;
1222 else if (next)
1223 av->calls_head = next->friend_number;
1224 else goto CLEAR;
1225
1226 if (next)
1227 next->prev = prev;
1228 else if (prev)
1229 av->calls_tail = prev->friend_number;
1230 else goto CLEAR;
1231
1232 av->calls[friend_number] = NULL;
1233 return next;
1234
1235CLEAR:
1236 av->calls_head = av->calls_tail = 0;
1237 free(av->calls);
1238 av->calls = NULL;
1239
1240 return NULL;
658} 1241}
659 1242
660/* Join a AV group (you need to have been invited first.) 1243bool call_prepare_transmission(ToxAVCall* call)
661 *
662 * returns group number on success
663 * returns -1 on failure.
664 *
665 * Audio data callback format (same as the one for toxav_add_av_groupchat()):
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)
667 *
668 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
669 */
670int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length,
671 void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *),
672 void *userdata)
673{ 1244{
674 Messenger *m = tox; 1245 /* Assumes mutex locked */
675 return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata); 1246
1247 if (call == NULL)
1248 return false;
1249
1250 ToxAV* av = call->av;
1251
1252 if (!av->acb.first && !av->vcb.first)
1253 /* It makes no sense to have CSession without callbacks */
1254 return false;
1255
1256 if (call->active) {
1257 LOGGER_WARNING("Call already active!\n");
1258 return true;
1259 }
1260
1261 if (create_recursive_mutex(call->mutex_audio) != 0)
1262 return false;
1263
1264 if (create_recursive_mutex(call->mutex_video) != 0)
1265 goto FAILURE_3;
1266
1267 if (create_recursive_mutex(call->mutex) != 0)
1268 goto FAILURE_2;
1269
1270
1271 { /* Prepare audio */
1272 call->audio.second = ac_new(av, call->friend_number, av->acb.first, av->acb.second);
1273 call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_number, call->audio.second, ac_queue_message);
1274
1275 if ( !call->audio.first || !call->audio.second ) {
1276 LOGGER_ERROR("Error while starting audio!\n");
1277 goto FAILURE;
1278 }
1279 }
1280
1281 { /* Prepare video */
1282 call->video.second = vc_new(av, call->friend_number, av->vcb.first, av->vcb.second, call->msi_call->peer_vfpsz);
1283 call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_number, call->video.second, vc_queue_message);
1284
1285 if ( !call->video.first || !call->video.second ) {
1286 LOGGER_ERROR("Error while starting video!\n");
1287 goto FAILURE;
1288 }
1289 }
1290
1291 call->active = 1;
1292 return true;
1293
1294FAILURE:
1295 rtp_kill(call->audio.first);
1296 ac_kill(call->audio.second);
1297 call->audio.first = NULL;
1298 call->audio.second = NULL;
1299 rtp_kill(call->video.first);
1300 vc_kill(call->video.second);
1301 call->video.first = NULL;
1302 call->video.second = NULL;
1303 pthread_mutex_destroy(call->mutex);
1304FAILURE_2:
1305 pthread_mutex_destroy(call->mutex_video);
1306FAILURE_3:
1307 pthread_mutex_destroy(call->mutex_audio);
1308 return false;
676} 1309}
677 1310
678/* Send audio to the group chat. 1311void call_kill_transmission(ToxAVCall* call)
679 *
680 * return 0 on success.
681 * return -1 on failure.
682 *
683 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
684 *
685 * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000)
686 * Valid number of channels are 1 or 2.
687 * Valid sample rates are 8000, 12000, 16000, 24000, or 48000.
688 *
689 * Recommended values are: samples = 960, channels = 1, sample_rate = 48000
690 */
691int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
692 unsigned int sample_rate)
693{ 1312{
694 Messenger *m = tox; 1313 if (call == NULL || call->active == 0)
695 return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate); 1314 return;
1315
1316 call->active = 0;
1317
1318 pthread_mutex_lock(call->mutex_audio);
1319 pthread_mutex_unlock(call->mutex_audio);
1320 pthread_mutex_lock(call->mutex_video);
1321 pthread_mutex_unlock(call->mutex_video);
1322 pthread_mutex_lock(call->mutex);
1323 pthread_mutex_unlock(call->mutex);
1324
1325 rtp_kill(call->audio.first);
1326 ac_kill(call->audio.second);
1327 call->audio.first = NULL;
1328 call->audio.second = NULL;
1329
1330 rtp_kill(call->video.first);
1331 vc_kill(call->video.second);
1332 call->video.first = NULL;
1333 call->video.second = NULL;
1334
1335 pthread_mutex_destroy(call->mutex_audio);
1336 pthread_mutex_destroy(call->mutex_video);
1337 pthread_mutex_destroy(call->mutex);
696} 1338}
697 1339
1340void ba_set(ToxAvBitrateAdapter* ba, uint32_t bit_rate)
1341{
1342 ba->bit_rate = bit_rate;
1343 ba->next_send = current_time_monotonic();
1344 ba->end_time = ~0;
1345 ba->next_send_interval = 1000;
1346 ba->active = true;
1347}
1348
1349bool ba_shoud_send_dummy(ToxAvBitrateAdapter* ba)
1350{
1351 if (!ba->active || ba->next_send > current_time_monotonic())
1352 return false;
1353
1354 ba->next_send_interval *= 0.8;
1355 ba->next_send = current_time_monotonic() + ba->next_send_interval;
1356
1357 return true;
1358} \ No newline at end of file
diff --git a/toxav/toxav.h b/toxav/toxav.h
index 7285f45c..0b9be241 100644
--- a/toxav/toxav.h
+++ b/toxav/toxav.h
@@ -1,329 +1,633 @@
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 `tox_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 synchronisation 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 */
62/**
63 * External Tox type.
64 */
40#ifndef TOX_DEFINED 65#ifndef TOX_DEFINED
41#define TOX_DEFINED 66#define TOX_DEFINED
42typedef struct Tox Tox; 67typedef struct Tox Tox;
43#endif 68#endif /* TOX_DEFINED */
44
45#define RTP_PAYLOAD_SIZE 65535
46
47
48/**
49 * Callbacks ids that handle the call states.
50 */
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 69
65/** 70/**
66 * Call type identifier. 71 * ToxAV.
67 */ 72 */
68typedef enum {
69 av_TypeAudio = 192,
70 av_TypeVideo
71} ToxAvCallType;
72
73
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
83/** 73/**
84 * Error indicators. Values under -20 are reserved for toxcore. 74 * The ToxAV instance type. Each ToxAV instance can be bound to only one Tox
85 */ 75 * instance, and Tox instance can have only one ToxAV instance. One must make
86typedef enum { 76 * sure to close ToxAV instance prior closing Tox instance otherwise undefined
87 av_ErrorNone = 0, 77 * behaviour occurs. Upon closing of ToxAV instance, all active calls will be
88 av_ErrorUnknown = -1, /* Unknown error */ 78 * forcibly terminated without notifying peers.
89 av_ErrorNoCall = -20, /* Trying to perform call action while not in a call */ 79 *
90 av_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/ 80 */
91 av_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */ 81#ifndef TOXAV_DEFINED
92 av_ErrorReachedCallLimit = -23, /* Cannot handle more calls */ 82#define TOXAV_DEFINED
93 av_ErrorInitializingCodecs = -30, /* Failed creating CSSession */ 83typedef struct ToxAV ToxAV;
94 av_ErrorSettingVideoResolution = -31, /* Error setting resolution */ 84#endif /* TOXAV_DEFINED */
95 av_ErrorSettingVideoBitrate = -32, /* Error setting bitrate */ 85/*******************************************************************************
96 av_ErrorSplittingVideoPayload = -33, /* Error splitting video payload */ 86 *
97 av_ErrorEncodingVideo = -34, /* vpx_codec_encode failed */ 87 * :: API version
98 av_ErrorEncodingAudio = -35, /* opus_encode failed */ 88 *
99 av_ErrorSendingPayload = -40, /* Sending lossy packet failed */ 89 ******************************************************************************/
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
107/** 90/**
108 * Locally supported capabilities. 91 * The major version number. Incremented when the API or ABI changes in an
92 * incompatible way.
109 */ 93 */
110typedef enum { 94#define TOXAV_VERSION_MAJOR 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
118/** 95/**
119 * Encoding settings. 96 * The minor version number. Incremented when functionality is added without
97 * breaking the API or ABI. Set to 0 when the major version number is
98 * incremented.
120 */ 99 */
121typedef struct _ToxAvCSettings { 100#define TOXAV_VERSION_MINOR 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
136/** 101/**
137 * Start new A/V session. There can only be one session at the time. 102 * The patch or revision number. Incremented when bugfixes are applied without
103 * changing any functionality or API or ABI.
138 */ 104 */
139ToxAv *toxav_new(Tox *messenger, int32_t max_calls); 105#define TOXAV_VERSION_PATCH 0u
140
141/** 106/**
142 * Remove A/V session. 107 * A macro to check at preprocessing time whether the client code is compatible
143 */ 108 * with the installed version of ToxAV.
144void toxav_kill(ToxAv *av); 109 */
145 110#define TOXAV_VERSION_IS_API_COMPATIBLE(MAJOR, MINOR, PATCH) \
111 (TOXAV_VERSION_MAJOR == MAJOR && \
112 (TOXAV_VERSION_MINOR > MINOR || \
113 (TOXAV_VERSION_MINOR == MINOR && \
114 TOXAV_VERSION_PATCH >= PATCH)))
146/** 115/**
147 * Returns the interval in milliseconds when the next toxav_do() should be called. 116 * A macro to make compilation fail if the client code is not compatible with
148 * If no call is active at the moment returns 200. 117 * the installed version of ToxAV.
149 */ 118 */
150uint32_t toxav_do_interval(ToxAv *av); 119#define TOXAV_VERSION_REQUIRE(MAJOR, MINOR, PATCH) \
151 120 typedef char toxav_required_version[TOXAV_IS_COMPATIBLE(MAJOR, MINOR, PATCH) ? 1 : -1]
152/** 121/**
153 * Main loop for the session. Best called right after tox_do(); 122 * A convenience macro to call toxav_version_is_compatible with the currently
123 * compiling API version.
154 */ 124 */
155void toxav_do(ToxAv *av); 125#define TOXAV_VERSION_IS_ABI_COMPATIBLE() \
156 126 toxav_version_is_compatible(TOXAV_VERSION_MAJOR, TOXAV_VERSION_MINOR, TOXAV_VERSION_PATCH)
157/** 127/**
158 * Register callback for call state. 128 * Return the major version number of the library. Can be used to display the
129 * ToxAV library version or to check whether the client is compatible with the
130 * dynamically linked version of ToxAV.
159 */ 131 */
160void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata); 132uint32_t toxav_version_major(void);
161
162/** 133/**
163 * Register callback for audio data. 134 * Return the minor version number of the library.
164 */ 135 */
165void toxav_register_audio_callback (ToxAv *av, ToxAvAudioCallback cb, void *userdata); 136uint32_t toxav_version_minor(void);
166
167/** 137/**
168 * Register callback for video data. 138 * Return the patch number of the library.
169 */ 139 */
170void toxav_register_video_callback (ToxAv *av, ToxAvVideoCallback cb, void *userdata); 140uint32_t toxav_version_patch(void);
171
172/** 141/**
173 * Call user. Use its friend_id. 142 * Return whether the compiled library version is compatible with the passed
143 * version numbers.
174 */ 144 */
175int toxav_call(ToxAv *av, 145bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch);
176 int32_t *call_index, 146/*******************************************************************************
177 int friend_id, 147 *
178 const ToxAvCSettings *csettings, 148 * :: Creation and destruction
179 int ringing_seconds); 149 *
180 150 ******************************************************************************/
151typedef enum TOXAV_ERR_NEW {
152 /**
153 * The function returned successfully.
154 */
155 TOXAV_ERR_NEW_OK,
156 /**
157 * One of the arguments to the function was NULL when it was not expected.
158 */
159 TOXAV_ERR_NEW_NULL,
160 /**
161 * Memory allocation failure while trying to allocate structures required for
162 * the A/V session.
163 */
164 TOXAV_ERR_NEW_MALLOC,
165 /**
166 * Attempted to create a second session for the same Tox instance.
167 */
168 TOXAV_ERR_NEW_MULTIPLE,
169} TOXAV_ERR_NEW;
181/** 170/**
182 * Hangup active call. 171 * Start new A/V session. There can only be only one session per Tox instance.
183 */ 172 */
184int toxav_hangup(ToxAv *av, int32_t call_index); 173ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error);
185
186/** 174/**
187 * Answer incoming call. Pass the csettings that you will use. 175 * Releases all resources associated with the A/V session.
176 *
177 * If any calls were ongoing, these will be forcibly terminated without
178 * notifying peers. After calling this function, no other functions may be
179 * called and the av pointer becomes invalid.
188 */ 180 */
189int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ); 181void toxav_kill(ToxAV *toxAV);
190
191/** 182/**
192 * Reject incoming call. 183 * Returns the Tox instance the A/V object was created for.
193 */ 184 */
194int toxav_reject(ToxAv *av, int32_t call_index, const char *reason); 185Tox *toxav_get_tox(const ToxAV *toxAV);
195 186/*******************************************************************************
187 *
188 * :: A/V event loop
189 *
190 ******************************************************************************/
196/** 191/**
197 * Cancel outgoing request. 192 * Returns the interval in milliseconds when the next toxav_iterate call should
193 * be. If no call is active at the moment, this function returns 200.
198 */ 194 */
199int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason); 195uint32_t toxav_iteration_interval(const ToxAV *toxAV);
200
201/** 196/**
202 * Notify peer that we are changing codec settings. 197 * Main loop for the session. This function needs to be called in intervals of
203 */ 198 * toxav_iteration_interval() milliseconds. It is best called in the separate
204int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings); 199 * thread from tox_iterate.
205 200 */
201void toxav_iterate(ToxAV *toxAV);
202/*******************************************************************************
203 *
204 * :: Call setup
205 *
206 ******************************************************************************/
207typedef enum TOXAV_ERR_CALL {
208 /**
209 * The function returned successfully.
210 */
211 TOXAV_ERR_CALL_OK,
212 /**
213 * A resource allocation error occurred while trying to create the structures
214 * required for the call.
215 */
216 TOXAV_ERR_CALL_MALLOC,
217 /**
218 * The friend number did not designate a valid friend.
219 */
220 TOXAV_ERR_CALL_FRIEND_NOT_FOUND,
221 /**
222 * The friend was valid, but not currently connected.
223 */
224 TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED,
225 /**
226 * Attempted to call a friend while already in an audio or video call with
227 * them.
228 */
229 TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL,
230 /**
231 * Audio or video bit rate is invalid.
232 */
233 TOXAV_ERR_CALL_INVALID_BIT_RATE,
234} TOXAV_ERR_CALL;
206/** 235/**
207 * Terminate transmission. Note that transmission will be 236 * Call a friend. This will start ringing the friend.
208 * terminated without informing remote peer. Usually called when we can't inform peer. 237 *
238 * It is the client's responsibility to stop ringing after a certain timeout,
239 * if such behaviour is desired. If the client does not stop ringing, the
240 * library will not stop until the friend is disconnected.
241 *
242 * @param friend_number The friend number of the friend that should be called.
243 * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
244 * audio sending.
245 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
246 * video sending.
209 */ 247 */
210int toxav_stop_call(ToxAv *av, int32_t call_index); 248bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL *error);
211
212/** 249/**
213 * Allocates transmission data. Must be call before calling toxav_prepare_* and toxav_send_*. 250 * The function type for the call callback.
214 * Also, it must be called when call is started 251 *
252 * @param friend_number The friend number from which the call is incoming.
253 * @param audio_enabled True if friend is sending audio.
254 * @param video_enabled True if friend is sending video.
215 */ 255 */
216int toxav_prepare_transmission(ToxAv *av, int32_t call_index, int support_video); 256typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data);
217
218/** 257/**
219 * Clears transmission data. Call this at the end of the transmission. 258 * Set the callback for the `call` event. Pass NULL to unset.
259 *
220 */ 260 */
221int toxav_kill_transmission(ToxAv *av, int32_t call_index); 261void toxav_callback_call(ToxAV *toxAV, toxav_call_cb *callback, void *user_data);
222 262typedef enum TOXAV_ERR_ANSWER {
263 /**
264 * The function returned successfully.
265 */
266 TOXAV_ERR_ANSWER_OK,
267 /**
268 * Failed to initialize codecs for call session. Note that codec initiation
269 * will fail if there is no receive callback registered for either audio or
270 * video.
271 */
272 TOXAV_ERR_ANSWER_CODEC_INITIALIZATION,
273 /**
274 * The friend number did not designate a valid friend.
275 */
276 TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND,
277 /**
278 * The friend was valid, but they are not currently trying to initiate a call.
279 * This is also returned if this client is already in a call with the friend.
280 */
281 TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING,
282 /**
283 * Audio or video bit rate is invalid.
284 */
285 TOXAV_ERR_ANSWER_INVALID_BIT_RATE,
286} TOXAV_ERR_ANSWER;
223/** 287/**
224 * Encode video frame. 288 * Accept an incoming call.
225 */ 289 *
226int toxav_prepare_video_frame ( ToxAv *av, 290 * If answering fails for any reason, the call will still be pending and it is
227 int32_t call_index, 291 * possible to try and answer it later.
228 uint8_t *dest, 292 *
229 int dest_max, 293 * @param friend_number The friend number of the friend that is calling.
230 vpx_image_t *input); 294 * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
295 * audio sending.
296 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
297 * video sending.
298 */
299bool toxav_answer(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER *error);
300/*******************************************************************************
301 *
302 * :: Call state graph
303 *
304 ******************************************************************************/
305enum TOXAV_CALL_STATE {
306 /**
307 * Set by the AV core if an error occurred on the remote end or if friend
308 * timed out. This is the final state after which no more state
309 * transitions can occur for the call. This call state will never be triggered
310 * in combination with other call states.
311 */
312 TOXAV_CALL_STATE_ERROR = 1,
313 /**
314 * The call has finished. This is the final state after which no more state
315 * transitions can occur for the call. This call state will never be
316 * triggered in combination with other call states.
317 */
318 TOXAV_CALL_STATE_FINISHED = 2,
319 /**
320 * The flag that marks that friend is sending audio.
321 */
322 TOXAV_CALL_STATE_SENDING_A = 4,
323 /**
324 * The flag that marks that friend is sending video.
325 */
326 TOXAV_CALL_STATE_SENDING_V = 8,
327 /**
328 * The flag that marks that friend is receiving audio.
329 */
330 TOXAV_CALL_STATE_RECEIVING_A = 16,
331 /**
332 * The flag that marks that friend is receiving video.
333 */
334 TOXAV_CALL_STATE_RECEIVING_V = 32,
335};
231 336
232/**
233 * Send encoded video packet.
234 */
235int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size);
236 337
237/** 338/**
238 * Encode audio frame. 339 * The function type for the call_state callback.
340 *
341 * @param friend_number The friend number for which the call state changed.
342 * @param state The new call state which is guaranteed to be different than
343 * the previous state. The state is set to 0 when the call is paused.
239 */ 344 */
240int toxav_prepare_audio_frame ( ToxAv *av, 345typedef void toxav_call_state_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t state, void *user_data);
241 int32_t call_index,
242 uint8_t *dest,
243 int dest_max,
244 const int16_t *frame,
245 int frame_size);
246
247/** 346/**
248 * Send encoded audio frame. 347 * Set the callback for the `call_state` event. Pass NULL to unset.
348 *
249 */ 349 */
250int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size); 350void toxav_callback_call_state(ToxAV *toxAV, toxav_call_state_cb *callback, void *user_data);
251 351/*******************************************************************************
352 *
353 * :: Call control
354 *
355 ******************************************************************************/
356typedef enum TOXAV_CALL_CONTROL {
357 /**
358 * Resume a previously paused call. Only valid if the pause was caused by this
359 * client, if not, this control is ignored. Not valid before the call is accepted.
360 */
361 TOXAV_CALL_CONTROL_RESUME,
362 /**
363 * Put a call on hold. Not valid before the call is accepted.
364 */
365 TOXAV_CALL_CONTROL_PAUSE,
366 /**
367 * Reject a call if it was not answered, yet. Cancel a call after it was
368 * answered.
369 */
370 TOXAV_CALL_CONTROL_CANCEL,
371 /**
372 * Request that the friend stops sending audio. Regardless of the friend's
373 * compliance, this will cause the audio_receive_frame event to stop being
374 * triggered on receiving an audio frame from the friend.
375 */
376 TOXAV_CALL_CONTROL_MUTE_AUDIO,
377 /**
378 * Calling this control will notify client to start sending audio again.
379 */
380 TOXAV_CALL_CONTROL_UNMUTE_AUDIO,
381 /**
382 * Request that the friend stops sending video. Regardless of the friend's
383 * compliance, this will cause the video_receive_frame event to stop being
384 * triggered on receiving an video frame from the friend.
385 */
386 TOXAV_CALL_CONTROL_HIDE_VIDEO,
387 /**
388 * Calling this control will notify client to start sending video again.
389 */
390 TOXAV_CALL_CONTROL_SHOW_VIDEO,
391} TOXAV_CALL_CONTROL;
392typedef enum TOXAV_ERR_CALL_CONTROL {
393 /**
394 * The function returned successfully.
395 */
396 TOXAV_ERR_CALL_CONTROL_OK,
397 /**
398 * The friend_number passed did not designate a valid friend.
399 */
400 TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND,
401 /**
402 * This client is currently not in a call with the friend. Before the call is
403 * answered, only CANCEL is a valid control.
404 */
405 TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL,
406 /**
407 * Happens if user tried to pause an already paused call or if trying to
408 * resume a call that is not paused.
409 */
410 TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION,
411} TOXAV_ERR_CALL_CONTROL;
252/** 412/**
253 * Get codec settings from the peer. These were exchanged during call initialization 413 * Sends a call control command to a friend.
254 * or when peer send us new csettings. 414 *
415 * @param friend_number The friend number of the friend this client is in a call
416 * with.
417 * @param control The control command to send.
418 *
419 * @return true on success.
255 */ 420 */
256int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ); 421bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error);
257 422/*******************************************************************************
423 *
424 * :: Controlling bit rates
425 *
426 ******************************************************************************/
427typedef enum TOXAV_ERR_SET_BIT_RATE {
428 /**
429 * The function returned successfully.
430 */
431 TOXAV_ERR_SET_BIT_RATE_OK,
432 /**
433 * The bit rate passed was not one of the supported values.
434 */
435 TOXAV_ERR_SET_BIT_RATE_INVALID,
436 /**
437 * The friend_number passed did not designate a valid friend.
438 */
439 TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_FOUND,
440 /**
441 * This client is currently not in a call with the friend.
442 */
443 TOXAV_ERR_SET_BIT_RATE_FRIEND_NOT_IN_CALL,
444} TOXAV_ERR_SET_BIT_RATE;
258/** 445/**
259 * Get friend id of peer participating in conversation. 446 * The function type for the audio_bit_rate_status callback.
260 */ 447 *
261int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ); 448 * @param friend_number The friend number of the friend for which to set the
262 449 * audio bit rate.
450 * @param stable Is the stream stable enough to keep the bit rate.
451 * Upon successful, non forceful, bit rate change, this is set to
452 * true and 'bit_rate' is set to new bit rate.
453 * The stable is set to false with bit_rate set to the unstable
454 * bit rate when either current stream is unstable with said bit rate
455 * or the non forceful change failed.
456 * @param bit_rate The bit rate in Kb/sec.
457 */
458typedef void toxav_audio_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data);
263/** 459/**
264 * Get current call state. 460 * Set the callback for the `audio_bit_rate_status` event. Pass NULL to unset.
461 *
265 */ 462 */
266ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index ); 463void toxav_callback_audio_bit_rate_status(ToxAV *toxAV, toxav_audio_bit_rate_status_cb *callback, void *user_data);
267
268/** 464/**
269 * Is certain capability supported. Used to determine if encoding/decoding is ready. 465 * Set the audio bit rate to be used in subsequent audio frames. If the passed
270 */ 466 * bit rate is the same as the current bit rate this function will return true
271int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ); 467 * without calling a callback. If there is an active non forceful setup with the
272 468 * passed audio bit rate and the new set request is forceful, the bit rate is
469 * forcefully set and the previous non forceful request is cancelled. The active
470 * non forceful setup will be canceled in favour of new non forceful setup.
471 *
472 * @param friend_number The friend number of the friend for which to set the
473 * audio bit rate.
474 * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
475 * audio sending.
476 * @param force True if the bit rate change is forceful.
477 *
478 */
479bool toxav_audio_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE *error);
273/** 480/**
274 * Returns tox reference. 481 * The function type for the video_bit_rate_status callback.
275 */ 482 *
276Tox *toxav_get_tox (ToxAv *av); 483 * @param friend_number The friend number of the friend for which to set the
277 484 * video bit rate.
485 * @param stable Is the stream stable enough to keep the bit rate.
486 * Upon successful, non forceful, bit rate change, this is set to
487 * true and 'bit_rate' is set to new bit rate.
488 * The stable is set to false with bit_rate set to the unstable
489 * bit rate when either current stream is unstable with said bit rate
490 * or the non forceful change failed.
491 * @param bit_rate The bit rate in Kb/sec.
492 */
493typedef void toxav_video_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, bool stable, uint32_t bit_rate, void *user_data);
278/** 494/**
279 * Returns number of active calls or -1 on error. 495 * Set the callback for the `video_bit_rate_status` event. Pass NULL to unset.
496 *
280 */ 497 */
281int toxav_get_active_count (ToxAv *av); 498void toxav_callback_video_bit_rate_status(ToxAV *toxAV, toxav_video_bit_rate_status_cb *callback, void *user_data);
282 499/**
283/* Create a new toxav group. 500 * Set the video bit rate to be used in subsequent video frames. If the passed
501 * bit rate is the same as the current bit rate this function will return true
502 * without calling a callback. If there is an active non forceful setup with the
503 * passed video bit rate and the new set request is forceful, the bit rate is
504 * forcefully set and the previous non forceful request is cancelled. The active
505 * non forceful setup will be canceled in favour of new non forceful setup.
284 * 506 *
285 * return group number on success. 507 * @param friend_number The friend number of the friend for which to set the
286 * return -1 on failure. 508 * video bit rate.
509 * @param audio_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
510 * video sending.
511 * @param force True if the bit rate change is forceful.
512 *
513 */
514bool toxav_video_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, bool force, TOXAV_ERR_SET_BIT_RATE *error);
515/*******************************************************************************
516 *
517 * :: A/V sending
287 * 518 *
288 * Audio data callback format: 519 ******************************************************************************/
289 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) 520typedef enum TOXAV_ERR_SEND_FRAME {
521 /**
522 * The function returned successfully.
523 */
524 TOXAV_ERR_SEND_FRAME_OK,
525 /**
526 * In case of video, one of Y, U, or V was NULL. In case of audio, the samples
527 * data pointer was NULL.
528 */
529 TOXAV_ERR_SEND_FRAME_NULL,
530 /**
531 * The friend_number passed did not designate a valid friend.
532 */
533 TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND,
534 /**
535 * This client is currently not in a call with the friend.
536 */
537 TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL,
538 /**
539 * One of the frame parameters was invalid. E.g. the resolution may be too
540 * small or too large, or the audio sampling rate may be unsupported.
541 */
542 TOXAV_ERR_SEND_FRAME_INVALID,
543 /**
544 * Failed to push frame through rtp interface.
545 */
546 TOXAV_ERR_SEND_FRAME_RTP_FAILED,
547} TOXAV_ERR_SEND_FRAME;
548/**
549 * Send an audio frame to a friend.
290 * 550 *
291 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 551 * The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]...
292 */ 552 * Meaning: sample 1 for channel 1, sample 1 for channel 2, ...
293int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, 553 * For mono audio, this has no meaning, every sample is subsequent. For stereo,
294 unsigned int, void *), void *userdata); 554 * this means the expected format is LRLRLR... with samples for left and right
295 555 * alternating.
296/* Join a AV group (you need to have been invited first.)
297 * 556 *
298 * returns group number on success 557 * @param friend_number The friend number of the friend to which to send an
299 * returns -1 on failure. 558 * audio frame.
559 * @param pcm An array of audio samples. The size of this array must be
560 * sample_count * channels.
561 * @param sample_count Number of samples in this frame. Valid numbers here are
562 * ((sample rate) * (audio length) / 1000), where audio length can be
563 * 2.5, 5, 10, 20, 40 or 60 millseconds.
564 * @param channels Number of audio channels. Supported values are 1 and 2.
565 * @param sampling_rate Audio sampling rate used in this frame. Valid sampling
566 * rates are 8000, 12000, 16000, 24000, or 48000.
567 */
568bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME *error);
569/**
570 * Send a video frame to a friend.
300 * 571 *
301 * Audio data callback format (same as the one for toxav_add_av_groupchat()): 572 * Y - plane should be of size: height * width
302 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) 573 * U - plane should be of size: (height/2) * (width/2)
574 * V - plane should be of size: (height/2) * (width/2)
303 * 575 *
304 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 576 * @param friend_number The friend number of the friend to which to send a video
305 */ 577 * frame.
306int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, 578 * @param width Width of the frame in pixels.
307 void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata); 579 * @param height Height of the frame in pixels.
308 580 * @param y Y (Luminance) plane data.
309/* Send audio to the group chat. 581 * @param u U (Chroma) plane data.
582 * @param v V (Chroma) plane data.
583 */
584bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error);
585/*******************************************************************************
586 *
587 * :: A/V receiving
310 * 588 *
311 * return 0 on success. 589 ******************************************************************************/
312 * return -1 on failure. 590/**
591 * The function type for the audio_receive_frame callback.
313 * 592 *
314 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 593 * @param friend_number The friend number of the friend who sent an audio frame.
594 * @param pcm An array of audio samples (sample_count * channels elements).
595 * @param sample_count The number of audio samples per channel in the PCM array.
596 * @param channels Number of audio channels.
597 * @param sampling_rate Sampling rate used in this frame.
315 * 598 *
316 * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000) 599 */
317 * Valid number of channels are 1 or 2. 600typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm, size_t sample_count, uint8_t channels, uint32_t sampling_rate, void *user_data);
318 * Valid sample rates are 8000, 12000, 16000, 24000, or 48000. 601/**
602 * Set the callback for the `audio_receive_frame` event. Pass NULL to unset.
319 * 603 *
320 * Recommended values are: samples = 960, channels = 1, sample_rate = 48000
321 */ 604 */
322int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, 605void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_cb *callback, void *user_data);
323 unsigned int sample_rate); 606/**
324 607 * The function type for the video_receive_frame callback.
325#ifdef __cplusplus 608 *
326} 609 * @param friend_number The friend number of the friend who sent a video frame.
610 * @param width Width of the frame in pixels.
611 * @param height Height of the frame in pixels.
612 * @param y
613 * @param u
614 * @param v Plane data.
615 * The size of plane data is derived from width and height where
616 * Y = MAX(width, abs(ystride)) * height,
617 * U = MAX(width/2, abs(ustride)) * (height/2) and
618 * V = MAX(width/2, abs(vstride)) * (height/2).
619 * @param ystride
620 * @param ustride
621 * @param vstride Strides data. Strides represent padding for each plane
622 * that may or may not be present. You must handle strides in
623 * your image processing code. Strides are negative if the
624 * image is bottom-up hence why you MUST abs() it when
625 * calculating plane buffer size.
626 */
627typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v, int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
628/**
629 * Set the callback for the `video_receive_frame` event. Pass NULL to unset.
630 *
631 */
632void toxav_callback_video_receive_frame(ToxAV *toxAV, toxav_video_receive_frame_cb *callback, void *user_data);
327#endif 633#endif
328
329#endif /* __TOXAV */
diff --git a/toxav/video.c b/toxav/video.c
new file mode 100644
index 00000000..ee49c0a1
--- /dev/null
+++ b/toxav/video.c
@@ -0,0 +1,375 @@
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#include <stdlib.h>
23#include <assert.h>
24
25#include "video.h"
26#include "msi.h"
27#include "rtp.h"
28
29#include "../toxcore/logger.h"
30#include "../toxcore/network.h"
31
32/* Good quality encode. */
33#define MAX_DECODE_TIME_US 0
34
35#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */
36#define VIDEOFRAME_HEADER_SIZE 0x2
37
38#define VIDEO_DECODE_BUFFER_SIZE 20
39
40typedef struct { uint16_t size; uint8_t data[]; } Payload;
41
42bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate);
43
44
45VCSession* vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb* cb, void* cb_data, uint32_t mvfpsz)
46{
47 VCSession *vc = calloc(sizeof(VCSession), 1);
48
49 if (!vc) {
50 LOGGER_WARNING("Allocation failed! Application might misbehave!");
51 return NULL;
52 }
53
54 if (create_recursive_mutex(vc->queue_mutex) != 0) {
55 LOGGER_WARNING("Failed to create recursive mutex!");
56 free(vc);
57 return NULL;
58 }
59
60 if ( !(vc->frame_buf = calloc(MAX_VIDEOFRAME_SIZE, 1)) )
61 goto BASE_CLEANUP;
62 if ( !(vc->split_video_frame = calloc(VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE, 1)) )
63 goto BASE_CLEANUP;
64 if ( !(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)) )
65 goto BASE_CLEANUP;
66
67 int rc = vpx_codec_dec_init_ver(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE,
68 NULL, 0, VPX_DECODER_ABI_VERSION);
69 if ( rc != VPX_CODEC_OK) {
70 LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
71 goto BASE_CLEANUP;
72 }
73
74 if (!create_video_encoder(vc->encoder, 500000)) {
75 vpx_codec_destroy(vc->decoder);
76 goto BASE_CLEANUP;
77 }
78 if (!create_video_encoder(vc->test_encoder, 500000)) {
79 vpx_codec_destroy(vc->encoder);
80 vpx_codec_destroy(vc->decoder);
81 goto BASE_CLEANUP;
82 }
83
84 vc->linfts = current_time_monotonic();
85 vc->lcfd = 60;
86 vc->vcb.first = cb;
87 vc->vcb.second = cb_data;
88 vc->friend_number = friend_number;
89 vc->peer_video_frame_piece_size = mvfpsz;
90 vc->av = av;
91
92 return vc;
93
94BASE_CLEANUP:
95 pthread_mutex_destroy(vc->queue_mutex);
96 rb_free(vc->vbuf_raw);
97 free(vc->split_video_frame);
98 free(vc->frame_buf);
99 free(vc);
100 return NULL;
101}
102void vc_kill(VCSession* vc)
103{
104 if (!vc)
105 return;
106
107 vpx_codec_destroy(vc->encoder);
108 vpx_codec_destroy(vc->test_encoder);
109 vpx_codec_destroy(vc->decoder);
110 rb_free(vc->vbuf_raw);
111 free(vc->split_video_frame);
112 free(vc->frame_buf);
113
114 pthread_mutex_destroy(vc->queue_mutex);
115
116 LOGGER_DEBUG("Terminated video handler: %p", vc);
117 free(vc);
118}
119void vc_do(VCSession* vc)
120{
121 if (!vc)
122 return;
123
124 Payload *p;
125 int rc;
126
127 pthread_mutex_lock(vc->queue_mutex);
128 if (rb_read(vc->vbuf_raw, (void**)&p)) {
129 pthread_mutex_unlock(vc->queue_mutex);
130
131 rc = vpx_codec_decode(vc->decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US);
132 free(p);
133
134 if (rc != VPX_CODEC_OK) {
135 LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc));
136 } else {
137 vpx_codec_iter_t iter = NULL;
138 vpx_image_t *dest = vpx_codec_get_frame(vc->decoder, &iter);
139
140 /* Play decoded images */
141 for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) {
142 if (vc->vcb.first)
143 vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h,
144 (const uint8_t*)dest->planes[0], (const uint8_t*)dest->planes[1], (const uint8_t*)dest->planes[2],
145 dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second);
146
147 vpx_img_free(dest);
148 }
149 }
150
151 return;
152 }
153 pthread_mutex_unlock(vc->queue_mutex);
154}
155void vc_init_video_splitter_cycle(VCSession* vc)
156{
157 if (!vc)
158 return;
159
160 vc->split_video_frame[0] = vc->frameid_out++;
161 vc->split_video_frame[1] = 0;
162}
163int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length)
164{
165 if (!vc)
166 return 0;
167
168 vc->processing_video_frame = payload;
169 vc->processing_video_frame_size = length;
170
171 return ((length - 1) / VIDEOFRAME_PIECE_SIZE) + 1;
172}
173const uint8_t* vc_iterate_split_video_frame(VCSession* vc, uint16_t* size)
174{
175 if (!vc || !size)
176 return NULL;
177
178 if (vc->processing_video_frame_size > VIDEOFRAME_PIECE_SIZE) {
179 memcpy(vc->split_video_frame + VIDEOFRAME_HEADER_SIZE,
180 vc->processing_video_frame,
181 VIDEOFRAME_PIECE_SIZE);
182
183 vc->processing_video_frame += VIDEOFRAME_PIECE_SIZE;
184 vc->processing_video_frame_size -= VIDEOFRAME_PIECE_SIZE;
185
186 *size = VIDEOFRAME_PIECE_SIZE + VIDEOFRAME_HEADER_SIZE;
187 } else {
188 memcpy(vc->split_video_frame + VIDEOFRAME_HEADER_SIZE,
189 vc->processing_video_frame,
190 vc->processing_video_frame_size);
191
192 *size = vc->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE;
193 }
194
195 vc->split_video_frame[1]++;
196
197 return vc->split_video_frame;
198}
199int vc_queue_message(void* vcp, struct RTPMessage_s *msg)
200{
201 /* This function does the reconstruction of video packets.
202 * See more info about video splitting in docs
203 */
204 if (!vcp || !msg)
205 return -1;
206
207 if ((msg->header->marker_payloadt & 0x7f) == (rtp_TypeVideo + 2) % 128) {
208 LOGGER_WARNING("Got dummy!");
209 rtp_free_msg(msg);
210 return 0;
211 }
212
213 if ((msg->header->marker_payloadt & 0x7f) != rtp_TypeVideo % 128) {
214 LOGGER_WARNING("Invalid payload type!");
215 rtp_free_msg(msg);
216 return -1;
217 }
218
219 VCSession* vc = vcp;
220
221 uint8_t *packet = msg->data;
222 uint32_t packet_size = msg->length;
223
224 if (packet_size < VIDEOFRAME_HEADER_SIZE)
225 goto end;
226
227 uint8_t diff = packet[0] - vc->frameid_in;
228
229 if (diff != 0) {
230 if (diff < 225) { /* New frame */
231 /* Flush last frames' data and get ready for this frame */
232 Payload *p = malloc(sizeof(Payload) + vc->frame_size);
233
234 if (p) {
235 pthread_mutex_lock(vc->queue_mutex);
236
237 if (rb_full(vc->vbuf_raw)) {
238 LOGGER_DEBUG("Dropped video frame");
239 Payload *tp;
240 rb_read(vc->vbuf_raw, (void**)&tp);
241 free(tp);
242 } else {
243 p->size = vc->frame_size;
244 memcpy(p->data, vc->frame_buf, vc->frame_size);
245 }
246
247 /* Calculate time took for peer to send us this frame */
248 uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
249 vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
250 vc->linfts = current_time_monotonic();
251
252 rb_write(vc->vbuf_raw, p);
253 pthread_mutex_unlock(vc->queue_mutex);
254 } else {
255 LOGGER_WARNING("Allocation failed! Program might misbehave!");
256 goto end;
257 }
258
259 vc->frameid_in = packet[0];
260 memset(vc->frame_buf, 0, vc->frame_size);
261 vc->frame_size = 0;
262
263 } else { /* Old frame; drop */
264 LOGGER_DEBUG("Old packet: %u", packet[0]);
265 goto end;
266 }
267 }
268
269 uint8_t piece_number = packet[1];
270
271 uint32_t length_before_piece = ((piece_number - 1) * vc->peer_video_frame_piece_size);
272 uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE);
273
274 if (framebuf_new_length > MAX_VIDEOFRAME_SIZE)
275 goto end;
276
277
278 /* Otherwise it's part of the frame so just process */
279 /* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */
280
281 memcpy(vc->frame_buf + length_before_piece,
282 packet + VIDEOFRAME_HEADER_SIZE,
283 packet_size - VIDEOFRAME_HEADER_SIZE);
284
285 if (framebuf_new_length > vc->frame_size)
286 vc->frame_size = framebuf_new_length;
287
288end:
289 rtp_free_msg(msg);
290 return 0;
291}
292int vc_reconfigure_encoder(VCSession* vc, int32_t bit_rate, uint16_t width, uint16_t height)
293{
294 if (!vc)
295 return -1;
296
297 vpx_codec_enc_cfg_t cfg = *vc->encoder->config.enc;
298 if (cfg.rc_target_bitrate == (uint32_t) bit_rate && cfg.g_w == width && cfg.g_h == height)
299 return 0; /* Nothing changed */
300
301 cfg.rc_target_bitrate = bit_rate;
302 cfg.g_w = width;
303 cfg.g_h = height;
304
305 int rc = vpx_codec_enc_config_set(vc->encoder, &cfg);
306 if ( rc != VPX_CODEC_OK) {
307 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
308 return -1;
309 }
310
311 return 0;
312}
313int vc_reconfigure_test_encoder(VCSession* vc, int32_t bit_rate, uint16_t width, uint16_t height)
314{
315 if (!vc)
316 return -1;
317
318 vpx_codec_enc_cfg_t cfg = *vc->test_encoder->config.enc;
319 if (cfg.rc_target_bitrate == (uint32_t) bit_rate && cfg.g_w == width && cfg.g_h == height)
320 return 0; /* Nothing changed */
321
322 cfg.rc_target_bitrate = bit_rate;
323 cfg.g_w = width;
324 cfg.g_h = height;
325
326 int rc = vpx_codec_enc_config_set(vc->test_encoder, &cfg);
327 if ( rc != VPX_CODEC_OK) {
328 LOGGER_ERROR("Failed to set test encoder control setting: %s", vpx_codec_err_to_string(rc));
329 return -1;
330 }
331
332 return 0;
333}
334
335
336
337bool create_video_encoder (vpx_codec_ctx_t* dest, int32_t bit_rate)
338{
339 assert(dest);
340
341 vpx_codec_enc_cfg_t cfg;
342 int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
343
344 if (rc != VPX_CODEC_OK) {
345 LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc));
346 return false;
347 }
348
349 rc = vpx_codec_enc_init_ver(dest, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0,
350 VPX_ENCODER_ABI_VERSION);
351
352 if ( rc != VPX_CODEC_OK) {
353 LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
354 return false;
355 }
356
357 cfg.rc_target_bitrate = bit_rate;
358 cfg.g_w = 800;
359 cfg.g_h = 600;
360 cfg.g_pass = VPX_RC_ONE_PASS;
361 cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
362 cfg.g_lag_in_frames = 0;
363 cfg.kf_min_dist = 0;
364 cfg.kf_max_dist = 48;
365 cfg.kf_mode = VPX_KF_AUTO;
366
367 rc = vpx_codec_control(dest, VP8E_SET_CPUUSED, 8);
368
369 if ( rc != VPX_CODEC_OK) {
370 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
371 vpx_codec_destroy(dest);
372 }
373
374 return true;
375} \ No newline at end of file
diff --git a/toxav/video.h b/toxav/video.h
new file mode 100644
index 00000000..96d3205d
--- /dev/null
+++ b/toxav/video.h
@@ -0,0 +1,113 @@
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_s;
40
41/*
42 * Base Video Codec session type.
43 */
44typedef struct VCSession_s {
45
46 /* encoding */
47 vpx_codec_ctx_t encoder[1];
48 vpx_codec_ctx_t test_encoder[1];
49 uint32_t frame_counter;
50 uint32_t test_frame_counter;
51
52 /* decoding */
53 vpx_codec_ctx_t decoder[1];
54 void *vbuf_raw; /* Un-decoded data */
55
56 /* Data handling */
57 uint8_t *frame_buf; /* buffer for split video payloads */
58 uint32_t frame_size; /* largest address written to in frame_buf for current input frame */
59 uint8_t frameid_in, frameid_out; /* id of input and output video frame */
60 uint64_t linfts; /* Last received frame time stamp */
61 uint32_t lcfd; /* Last calculated frame duration for incoming video payload */
62
63 /* Limits */
64 uint32_t peer_video_frame_piece_size;
65
66 /* Splitting */
67 uint8_t *split_video_frame;
68 const uint8_t *processing_video_frame;
69 uint16_t processing_video_frame_size;
70
71 ToxAV *av;
72 uint32_t friend_number;
73
74 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
75
76 pthread_mutex_t queue_mutex[1];
77} VCSession;
78
79/*
80 * Create new Video Codec session.
81 */
82VCSession* vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data, uint32_t mvfpsz);
83/*
84 * Kill the Video Codec session.
85 */
86void vc_kill(VCSession* vc);
87/*
88 * Do periodic work. Work is consisted out of decoding only.
89 */
90void vc_do(VCSession* vc);
91/*
92 * Set new video splitting cycle. This is requirement in order to send video packets.
93 */
94void vc_init_video_splitter_cycle(VCSession* vc);
95/*
96 * Update the video splitter cycle with new data.
97 */
98int vc_update_video_splitter_cycle(VCSession* vc, const uint8_t* payload, uint16_t length);
99/*
100 * Iterate over splitted cycle.
101 */
102const uint8_t *vc_iterate_split_video_frame(VCSession* vc, uint16_t *size);
103/*
104 * Queue new rtp message.
105 */
106int vc_queue_message(void *vcp, struct RTPMessage_s *msg);
107/*
108 * Set new values to the encoders.
109 */
110int vc_reconfigure_encoder(VCSession* vc, int32_t bit_rate, uint16_t width, uint16_t height);
111int vc_reconfigure_test_encoder(VCSession* vc, int32_t bit_rate, uint16_t width, uint16_t height);
112
113#endif /* VIDEO_H */ \ No newline at end of file
diff --git a/toxcore/logger.c b/toxcore/logger.c
index e8aef7e0..fc6a989a 100644
--- a/toxcore/logger.c
+++ b/toxcore/logger.c
@@ -200,12 +200,12 @@ void logger_write (Logger *log, LOG_LEVEL level, const char *file, int line, con
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/util.c b/toxcore/util.c
index 5a72c4a4..d6db946d 100644
--- a/toxcore/util.c
+++ b/toxcore/util.c
@@ -185,3 +185,76 @@ int create_recursive_mutex(pthread_mutex_t *mutex)
185 185
186 return 0; 186 return 0;
187} 187}
188
189
190struct RingBuffer {
191 uint16_t size; /* Max size */
192 uint16_t start;
193 uint16_t end;
194 void **data;
195};
196
197bool rb_full(const RingBuffer *b)
198{
199 return (b->end + 1) % b->size == b->start;
200}
201bool rb_empty(const RingBuffer *b)
202{
203 return b->end == b->start;
204}
205void* rb_write(RingBuffer *b, void *p)
206{
207 void* rc = NULL;
208 if ((b->end + 1) % b->size == b->start) /* full */
209 rc = b->data[b->start];
210
211 b->data[b->end] = p;
212 b->end = (b->end + 1) % b->size;
213
214 if (b->end == b->start)
215 b->start = (b->start + 1) % b->size;
216
217 return rc;
218}
219bool rb_read(RingBuffer *b, void **p)
220{
221 if (b->end == b->start) { /* Empty */
222 *p = NULL;
223 return false;
224 }
225
226 *p = b->data[b->start];
227 b->start = (b->start + 1) % b->size;
228 return true;
229}
230void rb_clear(RingBuffer *b)
231{
232 while (!rb_empty(b)) {
233 void *p;
234 rb_read(b, &p);
235 free(p);
236 }
237}
238RingBuffer *rb_new(int size)
239{
240 RingBuffer *buf = calloc(sizeof(RingBuffer), 1);
241
242 if (!buf) return NULL;
243
244 buf->size = size + 1; /* include empty elem */
245
246 if (!(buf->data = calloc(buf->size, sizeof(void *)))) {
247 free(buf);
248 return NULL;
249 }
250
251 return buf;
252}
253void rb_free(RingBuffer *b)
254{
255 if (b) {
256 rb_clear(b);
257 free(b->data);
258 free(b);
259 }
260} \ No newline at end of file
diff --git a/toxcore/util.h b/toxcore/util.h
index 7cd6bb8b..6c3d3b38 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();
@@ -53,6 +54,16 @@ typedef int (*load_state_callback_func)(void *outer, const uint8_t *data, uint32
53int load_state(load_state_callback_func load_state_callback, void *outer, 54int load_state(load_state_callback_func load_state_callback, void *outer,
54 const uint8_t *data, uint32_t length, uint16_t cookie_inner); 55 const uint8_t *data, uint32_t length, uint16_t cookie_inner);
55 56
57/* Returns -1 if failed or 0 if success */
56int create_recursive_mutex(pthread_mutex_t *mutex); 58int create_recursive_mutex(pthread_mutex_t *mutex);
57 59
60/* Ring buffer */
61typedef struct RingBuffer RingBuffer;
62bool rb_full(const RingBuffer *b);
63bool rb_empty(const RingBuffer *b);
64void* rb_write(RingBuffer* b, void* p);
65bool rb_read(RingBuffer* b, void** p);
66void rb_clear(RingBuffer *b);
67RingBuffer *rb_new(int size);
68void rb_free(RingBuffer *b);
58#endif /* __UTIL_H__ */ 69#endif /* __UTIL_H__ */