summaryrefslogtreecommitdiff
path: root/toxav
diff options
context:
space:
mode:
authormannol <eniz_vukovic@hotmail.com>2015-02-21 01:07:22 +0100
committermannol <eniz_vukovic@hotmail.com>2015-02-21 01:07:22 +0100
commit29601feb7656d1a75b4ab7cdb161f232f0ec92dc (patch)
tree9f09494355e792a62f9c5b52a152214030d9dfb9 /toxav
parentefe31ec92f476faffd6502714d05cce0a7dfadc7 (diff)
New msi protocol
Diffstat (limited to 'toxav')
-rw-r--r--toxav/av_test.c20
-rw-r--r--toxav/msi.c486
-rw-r--r--toxav/msi.h17
-rw-r--r--toxav/rtp.c14
-rw-r--r--toxav/rtp.h7
-rw-r--r--toxav/toxav.c432
-rw-r--r--toxav/toxav.h5
7 files changed, 445 insertions, 536 deletions
diff --git a/toxav/av_test.c b/toxav/av_test.c
index 3270f39c..594e7232 100644
--- a/toxav/av_test.c
+++ b/toxav/av_test.c
@@ -36,19 +36,11 @@ void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, TOXAV_CALL_STATE s
36{ 36{
37 printf("Handling CALL STATE callback: "); 37 printf("Handling CALL STATE callback: ");
38 38
39 if (((CallControl*)user_data)->ringing)
40 ((CallControl*)user_data)->ringing = false;
41
42 if (((CallControl*)user_data)->paused) 39 if (((CallControl*)user_data)->paused)
43 ((CallControl*)user_data)->paused = false; 40 ((CallControl*)user_data)->paused = false;
44 41
45 switch (state) 42 switch (state)
46 { 43 {
47 case TOXAV_CALL_STATE_RINGING: {
48 printf("Ringing");
49 ((CallControl*)user_data)->ringing = true;
50 } break;
51
52 case TOXAV_CALL_STATE_NOT_SENDING: { 44 case TOXAV_CALL_STATE_NOT_SENDING: {
53 printf("Not sending"); 45 printf("Not sending");
54 ((CallControl*)user_data)->sending = false; 46 ((CallControl*)user_data)->sending = false;
@@ -230,6 +222,7 @@ int main (int argc, char** argv)
230 exit(1); \ 222 exit(1); \
231 } \ 223 } \
232 BobCC.incoming = false; \ 224 BobCC.incoming = false; \
225 BobCC.sending = true; /* There is no more start callback when answering */\
233 } \ 226 } \
234 else if (AliceCC.sending && BobCC.sending) { \ 227 else if (AliceCC.sending && BobCC.sending) { \
235 /* TODO rtp */ \ 228 /* TODO rtp */ \
@@ -238,6 +231,7 @@ int main (int argc, char** argv)
238 \ 231 \
239 TOXAV_ERR_CALL_CONTROL rc; \ 232 TOXAV_ERR_CALL_CONTROL rc; \
240 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); \ 233 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); \
234 AliceCC.ended = true; /* There is no more end callback when hanging up */\
241 \ 235 \
242 if (rc != TOXAV_ERR_CALL_CONTROL_OK) { \ 236 if (rc != TOXAV_ERR_CALL_CONTROL_OK) { \
243 printf("toxav_call_control failed: %d\n", rc); \ 237 printf("toxav_call_control failed: %d\n", rc); \
@@ -252,13 +246,13 @@ int main (int argc, char** argv)
252 } 246 }
253 247
254 printf("\nTrying regular call (Audio and Video)...\n"); 248 printf("\nTrying regular call (Audio and Video)...\n");
255 REGULAR_CALL_FLOW(48, 4000); 249// REGULAR_CALL_FLOW(48, 4000);
256 250
257 printf("\nTrying regular call (Audio only)...\n"); 251 printf("\nTrying regular call (Audio only)...\n");
258 REGULAR_CALL_FLOW(48, 0); 252// REGULAR_CALL_FLOW(48, 0);
259 253
260 printf("\nTrying regular call (Video only)...\n"); 254 printf("\nTrying regular call (Video only)...\n");
261 REGULAR_CALL_FLOW(0, 4000); 255// REGULAR_CALL_FLOW(0, 4000);
262 256
263#undef REGULAR_CALL_FLOW 257#undef REGULAR_CALL_FLOW
264 258
@@ -292,7 +286,7 @@ int main (int argc, char** argv)
292 } 286 }
293 } 287 }
294 288
295 while (!AliceCC.ended || !BobCC.ended) 289 while (!AliceCC.ended)
296 iterate(Bsn, AliceAV, BobAV); 290 iterate(Bsn, AliceAV, BobAV);
297 291
298 printf("Success!\n"); 292 printf("Success!\n");
diff --git a/toxav/msi.c b/toxav/msi.c
index 16476364..f179a7ab 100644
--- a/toxav/msi.c
+++ b/toxav/msi.c
@@ -37,8 +37,6 @@
37 37
38#define MSI_MAXMSG_SIZE 256 38#define MSI_MAXMSG_SIZE 256
39 39
40/* TODO send error on any calloc or etc */
41
42/** 40/**
43 * Protocol: 41 * Protocol:
44 * 42 *
@@ -47,24 +45,17 @@
47 45
48typedef enum { 46typedef enum {
49 IDRequest = 1, 47 IDRequest = 1,
50 IDResponse,
51 IDError, 48 IDError,
52 IDCapabilities, 49 IDCapabilities,
53 IDVFPSZ, 50 IDVFPSZ,
54 51
55} MSIHeaderID; 52} MSIHeaderID;
56 53
57typedef enum {
58 requ_invite,
59 requ_start,
60 requ_reject,
61 requ_end,
62} MSIRequest;
63 54
64typedef enum { 55typedef enum {
65 resp_ringing, 56 requ_push,
66 resp_starting, 57 requ_pop,
67} MSIResponse; 58} MSIRequest;
68 59
69 60
70#define GENERIC_HEADER(header, val_type) \ 61#define GENERIC_HEADER(header, val_type) \
@@ -75,7 +66,6 @@ typedef struct { \
75 66
76 67
77GENERIC_HEADER ( Request, MSIRequest ); 68GENERIC_HEADER ( Request, MSIRequest );
78GENERIC_HEADER ( Response, MSIResponse );
79GENERIC_HEADER ( Error, MSIError ); 69GENERIC_HEADER ( Error, MSIError );
80GENERIC_HEADER ( Capabilities, uint8_t ); 70GENERIC_HEADER ( Capabilities, uint8_t );
81GENERIC_HEADER ( VFPSZ, uint16_t ); 71GENERIC_HEADER ( VFPSZ, uint16_t );
@@ -83,29 +73,24 @@ GENERIC_HEADER ( VFPSZ, uint16_t );
83 73
84typedef struct { 74typedef struct {
85 MSIHeaderRequest request; 75 MSIHeaderRequest request;
86 MSIHeaderResponse response;
87 MSIHeaderError error; 76 MSIHeaderError error;
88 MSIHeaderCapabilities capabilities; 77 MSIHeaderCapabilities capabilities;
89 MSIHeaderVFPSZ vfpsz; /* Video frame piece size. NOTE: Value must be in network b-order */ 78 MSIHeaderVFPSZ vfpsz; /* Video frame piece size. NOTE: Value must be in network b-order */
90} MSIMessage; 79} MSIMessage;
91 80
92 81
82void msg_init (MSIMessage *dest, MSIRequest request);
93int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ); 83int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length );
94uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length ); 84uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length );
95int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg ); 85int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg );
96int send_error ( Messenger* m, uint32_t friend_id, MSIError error ); 86int send_error ( Messenger* m, uint32_t friend_id, MSIError error );
97static void invoke_callback(MSICall* call, MSICallbackID cb); 87static int invoke_callback(MSICall* call, MSICallbackID cb);
98static MSICall *get_call ( MSISession *session, uint32_t friend_id ); 88static MSICall *get_call ( MSISession *session, uint32_t friend_id );
99MSICall *new_call ( MSISession *session, uint32_t friend_id ); 89MSICall *new_call ( MSISession *session, uint32_t friend_id );
100void kill_call ( MSICall *call ); 90void kill_call ( MSICall *call );
101void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data); 91void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data);
102int handle_recv_invite ( MSICall *call, const MSIMessage *msg ); 92void handle_push ( MSICall *call, const MSIMessage *msg );
103int handle_recv_start ( MSICall *call, const MSIMessage *msg ); 93void handle_pop ( MSICall *call, const MSIMessage *msg );
104int handle_recv_reject ( MSICall *call, const MSIMessage *msg );
105int handle_recv_end ( MSICall *call, const MSIMessage *msg );
106int handle_recv_ringing ( MSICall *call, const MSIMessage *msg );
107int handle_recv_starting ( MSICall *call, const MSIMessage *msg );
108int handle_recv_error ( MSICall *call, const MSIMessage *msg );
109void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object ); 94void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object );
110 95
111 96
@@ -157,13 +142,12 @@ int msi_kill ( MSISession *session )
157 pthread_mutex_lock(session->mutex); 142 pthread_mutex_lock(session->mutex);
158 143
159 if (session->calls) { 144 if (session->calls) {
160 MSIMessage msg_end; 145 MSIMessage msg;
161 msg_end.request.exists = true; 146 msg_init(&msg, requ_pop);
162 msg_end.request.value = requ_end;
163 147
164 MSICall* it = get_call(session, session->calls_head); 148 MSICall* it = get_call(session, session->calls_head);
165 for (; it; it = it->next) { 149 for (; it; it = it->next) {
166 send_message(session->messenger, it->friend_id, &msg_end); 150 send_message(session->messenger, it->friend_id, &msg);
167 kill_call(it); /* This will eventually free session->calls */ 151 kill_call(it); /* This will eventually free session->calls */
168 } 152 }
169 } 153 }
@@ -191,17 +175,16 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_
191 175
192 (*call)->self_capabilities = capabilities; 176 (*call)->self_capabilities = capabilities;
193 177
194 MSIMessage msg_invite; 178 MSIMessage msg;
195 msg_invite.request.exists = true; 179 msg_init(&msg, requ_push);
196 msg_invite.request.value = requ_invite;
197 180
198 msg_invite.capabilities.exists = true; 181 msg.capabilities.exists = true;
199 msg_invite.capabilities.value = capabilities; 182 msg.capabilities.value = capabilities;
200 183
201 msg_invite.vfpsz.exists = true; 184 msg.vfpsz.exists = true;
202 msg_invite.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE); 185 msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE);
203 186
204 send_message ( (*call)->session->messenger, (*call)->friend_id, &msg_invite ); 187 send_message ( (*call)->session->messenger, (*call)->friend_id, &msg );
205 188
206 (*call)->state = msi_CallRequesting; 189 (*call)->state = msi_CallRequesting;
207 190
@@ -212,10 +195,10 @@ int msi_hangup ( MSICall* call )
212{ 195{
213 LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_id); 196 LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_id);
214 197
215 MSIMessage msg_end; 198 MSIMessage msg;
216 msg_end.request.exists = true; 199 msg_init(&msg, requ_pop);
217 msg_end.request.value = requ_end; 200
218 send_message ( call->session->messenger, call->friend_id, &msg_end ); 201 send_message ( call->session->messenger, call->friend_id, &msg );
219 202
220 kill_call(call); 203 kill_call(call);
221 return 0; 204 return 0;
@@ -225,65 +208,68 @@ int msi_answer ( MSICall* call, uint8_t capabilities )
225 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_id); 208 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_id);
226 209
227 if ( call->state != msi_CallRequested ) { 210 if ( call->state != msi_CallRequested ) {
211 /* Though sending in invalid state will not cause anything wierd
212 * Its better to not do it like a maniac */
228 LOGGER_ERROR("Call is in invalid state!"); 213 LOGGER_ERROR("Call is in invalid state!");
229 return -1; 214 return -1;
230 } 215 }
231 216
232 call->self_capabilities = capabilities; 217 call->self_capabilities = capabilities;
233 218
234 MSIMessage msg_starting; 219 MSIMessage msg;
235 msg_starting.response.exists = true; 220 msg_init(&msg, requ_push);
236 msg_starting.response.value = resp_starting;
237 221
238 msg_starting.capabilities.exists = true; 222 msg.capabilities.exists = true;
239 msg_starting.capabilities.value = capabilities; 223 msg.capabilities.value = capabilities;
240 224
241 msg_starting.vfpsz.exists = true; 225 msg.vfpsz.exists = true;
242 msg_starting.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE); 226 msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE);
243 227
244 send_message ( call->session->messenger, call->friend_id, &msg_starting ); 228 send_message ( call->session->messenger, call->friend_id, &msg );
229
230 call->state = msi_CallActive;
245 231
246 return 0; 232 return 0;
247} 233}
248int msi_reject ( MSICall* call ) 234int msi_change_capabilities( MSICall* call, uint8_t capabilities )
249{ 235{
250 LOGGER_DEBUG("Session: %p Rejecting call with friend: %u", call->session, call->friend_id); 236 LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_id);
251 237
252 if ( call->state != msi_CallRequested ) { 238 if ( call->state != msi_CallActive ) {
239 /* Sending capabilities change can cause error on other side if
240 * the call is not active since we don't send header 'vfpsz'.
241 * If we were to send 'vfpsz' while call is active it would be
242 * ignored. However, if call is not active peer will expect
243 * the said header on 'push' so that it could handle the call
244 * like new. TODO: explain this better
245 */
253 LOGGER_ERROR("Call is in invalid state!"); 246 LOGGER_ERROR("Call is in invalid state!");
254 return -1; 247 return -1;
255 } 248 }
256 249
257 MSIMessage msg_reject;
258 msg_reject.request.exists = true;
259 msg_reject.request.value = requ_reject;
260
261 send_message ( call->session->messenger, call->friend_id, &msg_reject );
262
263 return 0;
264}
265int msi_change_csettings( MSICall* call, uint8_t capabilities )
266{
267 call->self_capabilities = capabilities; 250 call->self_capabilities = capabilities;
268 251
269 MSIMessage msg_invite; 252 MSIMessage msg;
270 msg_invite.request.exists = true; 253 msg_init(&msg, requ_push);
271 msg_invite.request.value = requ_invite;
272 254
273 msg_invite.capabilities.exists = true; 255 msg.capabilities.exists = true;
274 msg_invite.capabilities.value = capabilities; 256 msg.capabilities.value = capabilities;
275 257
276 send_message ( call->session->messenger, call->friend_id, &msg_invite ); 258 send_message ( call->session->messenger, call->friend_id, &msg );
277 259
278 return 0; 260 return 0;
279} 261}
280 262
281 263
282
283/** 264/**
284 * Private functions 265 * Private functions
285 */ 266 */
286 267void msg_init(MSIMessage* dest, MSIRequest request)
268{
269 memset(dest, 0, sizeof(*dest));
270 dest->request.exists = true;
271 dest->request.value = request;
272}
287int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) 273int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
288{ 274{
289 /* Parse raw data received from socket into MSIMessage struct */ 275 /* Parse raw data received from socket into MSIMessage struct */
@@ -314,7 +300,9 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
314 LOGGER_ERROR("Invalid end byte"); 300 LOGGER_ERROR("Invalid end byte");
315 return -1; 301 return -1;
316 } 302 }
317 303
304 memset(dest, 0, sizeof(*dest));
305
318 const uint8_t *it = data; 306 const uint8_t *it = data;
319 int size_constraint = length; 307 int size_constraint = length;
320 308
@@ -322,17 +310,10 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
322 switch (*it) { 310 switch (*it) {
323 case IDRequest: 311 case IDRequest:
324 CHECK_SIZE(it, size_constraint, 1); 312 CHECK_SIZE(it, size_constraint, 1);
325 CHECK_ENUM_HIGH(it, requ_end); 313 CHECK_ENUM_HIGH(it, requ_pop);
326 SET_UINT8(it, dest->request); 314 SET_UINT8(it, dest->request);
327 break; 315 break;
328 316
329 case IDResponse:
330 CHECK_SIZE(it, size_constraint, 1);
331 CHECK_ENUM_HIGH(it, resp_starting);
332 SET_UINT8(it, dest->response);
333 it += 3;
334 break;
335
336 case IDError: 317 case IDError:
337 CHECK_SIZE(it, size_constraint, 1); 318 CHECK_SIZE(it, size_constraint, 1);
338 CHECK_ENUM_HIGH(it, msi_ErrUndisclosed); 319 CHECK_ENUM_HIGH(it, msi_ErrUndisclosed);
@@ -356,6 +337,11 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
356 } 337 }
357 } 338 }
358 339
340 if (dest->request.exists == false) {
341 LOGGER_ERROR("Invalid request field!");
342 return -1;
343 }
344
359 return 0; 345 return 0;
360 346
361#undef CHECK_SIZE 347#undef CHECK_SIZE
@@ -395,17 +381,15 @@ int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg )
395 uint8_t cast = msg->request.value; 381 uint8_t cast = msg->request.value;
396 it = msg_parse_header_out(IDRequest, it, &cast, 382 it = msg_parse_header_out(IDRequest, it, &cast,
397 sizeof(cast), &size); 383 sizeof(cast), &size);
398 } 384 } else {
399 385 LOGGER_DEBUG("Must have request field");
400 if (msg->response.exists) { 386 return -1;
401 uint8_t cast = msg->response.value;
402 it = msg_parse_header_out(IDResponse, it, &cast,
403 sizeof(cast), &size);
404 } 387 }
405 388
406 if (msg->error.exists) { 389 if (msg->error.exists) {
407 it = msg_parse_header_out(IDError, it, &msg->error.value, 390 uint8_t cast = msg->error.value;
408 sizeof(msg->error.value), &size); 391 it = msg_parse_header_out(IDError, it, &cast,
392 sizeof(cast), &size);
409 } 393 }
410 394
411 if (msg->capabilities.exists) { 395 if (msg->capabilities.exists) {
@@ -418,14 +402,14 @@ int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg )
418 sizeof(msg->vfpsz.value), &size); 402 sizeof(msg->vfpsz.value), &size);
419 } 403 }
420 404
421 *it = 0;
422 size ++;
423
424 if ( it == parsed ) { 405 if ( it == parsed ) {
425 LOGGER_WARNING("Parsing message failed; empty message"); 406 LOGGER_WARNING("Parsing message failed; empty message");
426 return -1; 407 return -1;
427 } 408 }
428 409
410 *it = 0;
411 size ++;
412
429 if ( m_msi_packet(m, friend_id, parsed, size) ) { 413 if ( m_msi_packet(m, friend_id, parsed, size) ) {
430 LOGGER_DEBUG("Sent message"); 414 LOGGER_DEBUG("Sent message");
431 return 0; 415 return 0;
@@ -440,22 +424,36 @@ int send_error ( Messenger* m, uint32_t friend_id, MSIError error )
440 424
441 LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_id); 425 LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_id);
442 426
443 MSIMessage msg_error; 427 MSIMessage msg;
428 msg_init(&msg, requ_pop);
444 429
445 msg_error.error.exists = true; 430 msg.error.exists = true;
446 msg_error.error.value = error; 431 msg.error.value = error;
447 432
448 send_message ( m, friend_id, &msg_error ); 433 send_message ( m, friend_id, &msg );
449 return 0; 434 return 0;
450} 435}
451static void invoke_callback(MSICall* call, MSICallbackID cb) 436int invoke_callback(MSICall* call, MSICallbackID cb)
452{ 437{
453 assert(call); 438 assert(call);
454 439
455 if ( call->session->callbacks[cb] ) { 440 if ( call->session->callbacks[cb] ) {
456 LOGGER_DEBUG("Invoking callback function: %d", cb); 441 LOGGER_DEBUG("Invoking callback function: %d", cb);
457 call->session->callbacks[cb] ( call->session->av, call ); 442 if ( call->session->callbacks[cb] ( call->session->av, call ) != 0 ) {
443 LOGGER_WARNING("Callback handling failed, sending error");
444 goto FAILURE;
445 }
446
447 return 0;
458 } 448 }
449
450FAILURE:
451 /* If no callback present or error happened while handling,
452 * an error message will be send to friend
453 */
454
455 call->error = msi_HandleError;
456 return -1;
459} 457}
460static MSICall *get_call ( MSISession *session, uint32_t friend_id ) 458static MSICall *get_call ( MSISession *session, uint32_t friend_id )
461{ 459{
@@ -563,7 +561,7 @@ void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data)
563 if (call == NULL) 561 if (call == NULL)
564 return; 562 return;
565 563
566 invoke_callback(call, msi_OnPeerTimeout); 564 invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */
567 kill_call(call); 565 kill_call(call);
568 } 566 }
569 break; 567 break;
@@ -572,205 +570,120 @@ void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data)
572 break; 570 break;
573 } 571 }
574} 572}
575int handle_recv_invite ( MSICall *call, const MSIMessage *msg ) 573void handle_push ( MSICall *call, const MSIMessage *msg )
576{ 574{
577 assert(call); 575 assert(call);
578 576
579 MSISession* session = call->session; 577 MSISession* session = call->session;
580 578
581 LOGGER_DEBUG("Session: %p Handling 'invite' friend: %d", call->session, call->friend_id); 579 LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_id);
582 580
583 if (!msg->capabilities.exists) { 581 if (!msg->capabilities.exists) {
584 LOGGER_WARNING("Session: %p Invalid capabilities on 'invite'"); 582 LOGGER_WARNING("Session: %p Invalid capabilities on 'push'");
585 call->error = msi_InvalidMessage; 583 call->error = msi_InvalidMessage;
586 return -1; 584 goto FAILURE;
587 } 585 }
588 586
589 MSIMessage response; 587 if (call->state != msi_CallActive) {
590 response.response.exists = true;
591
592 if ( call->state == msi_CallRequesting ) {
593 /* The rare glare case.
594 * Send starting and wait for starting by the other side.
595 * The peer will do the same.
596 * When you receive starting from peer send started.
597 * Don't notice the app until the start is received.
598 */
599
600 LOGGER_DEBUG("Glare detected!");
601
602 if (!msg->vfpsz.exists) { 588 if (!msg->vfpsz.exists) {
603 LOGGER_WARNING("Session: %p Invalid mvfpsz on 'invite'"); 589 LOGGER_WARNING("Session: %p Invalid vfpsz on 'push'");
604 call->error = msi_InvalidMessage; 590 call->error = msi_InvalidMessage;
605 return -1; 591 goto FAILURE;
606 } 592 }
607 593
608 call->peer_capabilities = msg->capabilities.value; 594 /* Sending video frame piece size is ignored when call is active */
609 call->peer_vfpsz = ntohs(msg->vfpsz.value); 595 call->peer_vfpsz = ntohs(msg->vfpsz.value);
596 }
597
598
599 switch (call->state) {
600 case msi_CallInactive: {
601 LOGGER_INFO("Friend is calling us");
602
603 /* Call requested */
604 call->peer_capabilities = msg->capabilities.value;
605 call->state = msi_CallRequested;
606
607 if ( invoke_callback(call, msi_OnInvite) == -1 )
608 goto FAILURE;
609
610 } break;
610 611
611 /* Send response */ 612 case msi_CallActive: {
612 response.response.value = resp_starting; 613 /* Only act if capabilities changed */
613 send_message ( call->session->messenger, call->friend_id, &response ); 614 if ( call->peer_capabilities != msg->capabilities.value) {
614 615 LOGGER_INFO("Friend is changing capabilities");
615 return 0; 616
616 } else if ( call->state == msi_CallActive ) { 617 call->peer_capabilities = msg->capabilities.value;
617 /* Changing capabilities. 618 if ( invoke_callback(call, msi_OnCapabilities) == -1 )
618 * We send starting but no response is expected. 619 goto FAILURE;
619 * WARNING: if start is sent call is terminated with an error 620 }
620 */ 621 } break;
621 LOGGER_DEBUG("Peer is changing capabilities");
622
623 /* Send response */
624 response.response.value = resp_starting;
625 send_message ( call->session->messenger, call->friend_id, &response );
626 622
627 if ( call->peer_capabilities != msg->capabilities.value) { 623 case msi_CallRequesting: {
628 /* Only invoke callback if capabilities changed */ 624 LOGGER_INFO("Friend answered our call");
625
626 /* Call started */
629 call->peer_capabilities = msg->capabilities.value; 627 call->peer_capabilities = msg->capabilities.value;
630 invoke_callback(call, msi_OnCapabilities); 628 call->state = msi_CallActive;
631 } 629
630 if ( invoke_callback(call, msi_OnStart) == -1 )
631 goto FAILURE;
632 } break;
632 633
633 return 0; 634 case msi_CallRequested: {
634 } 635 /* Consecutive pushes during initialization state are ignored */
635 636 LOGGER_WARNING("Consecutive push");
636 if (!msg->vfpsz.exists) { 637 } break;
637 LOGGER_WARNING("Session: %p Invalid mvfpsz on 'invite'");
638 call->error = msi_InvalidMessage;
639 return -1;
640 } 638 }
641 639
642 call->peer_capabilities = msg->capabilities.value; 640 return;
643 call->peer_vfpsz = ntohs(msg->vfpsz.value);
644 call->state = msi_CallRequested;
645
646 /* Send response */
647 response.response.value = resp_ringing;
648 send_message ( call->session->messenger, call->friend_id, &response );
649
650
651 invoke_callback(call, msi_OnInvite);
652 return 0;
653}
654int handle_recv_start ( MSICall *call, const MSIMessage *msg )
655{
656 assert(call);
657
658 if ( call->state != msi_CallRequested || call->state != msi_CallRequesting ) {
659 LOGGER_WARNING("Session: %p Invalid call state on 'start'");
660 call->error = msi_InvalidState;
661 return -1;
662 }
663
664 (void)msg;
665
666 LOGGER_DEBUG("Session: %p Handling 'start', friend id: %d", call->session, call->friend_id );
667
668 call->state = msi_CallActive;
669 invoke_callback(call, msi_OnStart);
670
671 return 0;
672}
673int handle_recv_reject ( MSICall *call, const MSIMessage *msg )
674{
675 assert(call);
676
677 (void)msg;
678
679 if ( call->state != msi_CallRequesting ) {
680 LOGGER_WARNING("Session: %p Invalid call state on 'reject'");
681 call->error = msi_InvalidState;
682 return -1;
683 }
684 641
685 LOGGER_DEBUG("Session: %p Handling 'reject', friend id: %u", call->session, call->friend_id); 642FAILURE:
686 643 send_error(call->session->messenger, call->friend_id, call->error);
687 invoke_callback(call, msi_OnReject);
688 kill_call(call); 644 kill_call(call);
689
690 return 0;
691}
692int handle_recv_end ( MSICall *call, const MSIMessage *msg )
693{
694 assert(call);
695
696 (void)msg;
697
698 LOGGER_DEBUG("Session: %p Handling 'end', friend id: %d", call->session, call->friend_id);
699
700 invoke_callback(call, msi_OnEnd);
701 kill_call ( call );
702
703 return 0;
704} 645}
705int handle_recv_ringing ( MSICall *call, const MSIMessage *msg ) 646void handle_pop ( MSICall *call, const MSIMessage *msg )
706{ 647{
707 assert(call); 648 assert(call);
708 649
709 (void)msg; 650 LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_id);
710
711 if ( call->state != msi_CallRequesting ) {
712 LOGGER_WARNING("Session: %p Invalid call state on 'ringing'");
713 call->error = msi_InvalidState;
714 return -1;
715 }
716
717 LOGGER_DEBUG("Session: %p Handling 'ringing' friend id: %d", call->session, call->friend_id );
718
719 invoke_callback(call, msi_OnRinging);
720 return 0;
721}
722int handle_recv_starting ( MSICall *call, const MSIMessage *msg )
723{
724 assert(call);
725 651
726 if ( call->state == msi_CallActive ) { 652 /* callback errors are ignored */
727 LOGGER_DEBUG("Capabilities change confirmed");
728 return 0;
729 } else if ( call->state != msi_CallRequested || call->state != msi_CallRequesting ) {
730 LOGGER_WARNING("Session: %p Invalid call state on 'starting'");
731 call->error = msi_InvalidState;
732 return -1;
733 }
734 653
735 if (call->state == msi_CallRequesting) { 654 if (msg->error.exists) {
736 if (!msg->capabilities.exists) { 655 LOGGER_WARNING("Friend detected an error: %d", msg->error.value);
737 LOGGER_WARNING("Session: %p Invalid capabilities on 'starting'"); 656 call->error = msg->error.value;
738 call->error = msi_InvalidParam; 657 invoke_callback(call, msi_OnError);
739 return -1;
740 }
741 658
742 if (!msg->vfpsz.exists) { 659 } else switch (call->state) {
743 LOGGER_WARNING("Session: %p Invalid mvfpsz on 'invite'"); 660 case msi_CallInactive: {
744 call->error = msi_InvalidParam; 661 LOGGER_ERROR("Handling what should be impossible case");
745 return -1; 662 abort();
746 } 663 } break;
747 664
748 call->peer_capabilities = msg->capabilities.value; 665 case msi_CallActive: {
749 call->peer_vfpsz = ntohs(msg->vfpsz.value); 666 /* Hangup */
750 call->state = msi_CallActive; 667 LOGGER_INFO("Friend hung up on us");
668 invoke_callback(call, msi_OnEnd);
669 } break;
751 670
752 invoke_callback(call, msi_OnStart); 671 case msi_CallRequesting: {
672 /* Reject */
673 LOGGER_INFO("Friend rejected our call");
674 invoke_callback(call, msi_OnEnd);
675 } break;
676
677 case msi_CallRequested: {
678 /* Cancel */
679 LOGGER_INFO("Friend canceled call invite");
680 invoke_callback(call, msi_OnEnd);
681 } break;
753 } 682 }
754 /* Otherwise it's a glare case so don't start until 'start' is recved */
755
756 /* Send start in either case (glare or normal) */
757 MSIMessage msg_start;
758 msg_start.request.exists = true;
759 msg_start.request.value = requ_start;
760 send_message ( call->session->messenger, call->friend_id, &msg_start );
761 683
762 return 0; 684 kill_call ( call );
763}
764int handle_recv_error ( MSICall *call, const MSIMessage *msg )
765{
766 assert(call);
767
768 LOGGER_DEBUG("Session: %p Handling 'error' friend id: %d", call->session, call->friend_id );
769
770 call->error = msg->error.value;
771 invoke_callback(call, msi_OnError);
772 685
773 return 0; 686 return;
774} 687}
775void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object ) 688void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object )
776{ 689{
@@ -779,8 +692,6 @@ void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint1
779 MSISession *session = object; 692 MSISession *session = object;
780 MSIMessage msg; 693 MSIMessage msg;
781 694
782 int rc = 0;
783
784 if ( msg_parse_in ( &msg, data, length ) == -1 ) { 695 if ( msg_parse_in ( &msg, data, length ) == -1 ) {
785 LOGGER_WARNING("Error parsing message"); 696 LOGGER_WARNING("Error parsing message");
786 send_error(m, friend_id, msi_InvalidMessage); 697 send_error(m, friend_id, msi_InvalidMessage);
@@ -792,7 +703,7 @@ void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint1
792 MSICall *call = get_call(session, friend_id); 703 MSICall *call = get_call(session, friend_id);
793 704
794 if (call == NULL) { 705 if (call == NULL) {
795 if (msg.request.value != requ_invite) { 706 if (msg.request.value != requ_push) {
796 send_error(m, friend_id, msi_StrayMessage); 707 send_error(m, friend_id, msi_StrayMessage);
797 return; 708 return;
798 } 709 }
@@ -805,49 +716,8 @@ void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint1
805 } 716 }
806 717
807 718
808 /* Now handle message */ 719 if (msg.request.value == requ_push)
809 if ( msg.request.exists ) { /* Handle request */ 720 handle_push(call, &msg);
810 switch (msg.request.value) { 721 else
811 case requ_invite: 722 handle_pop(call, &msg); /* always kills the call */
812 rc = handle_recv_invite ( call, &msg ); 723} \ No newline at end of file
813 break;
814
815 case requ_start:
816 rc = handle_recv_start ( call, &msg );
817 break;
818
819 case requ_reject:
820 rc = handle_recv_reject ( call, &msg );
821 break;
822
823 case requ_end:
824 rc = handle_recv_end ( call, &msg );
825 break;
826 }
827 } else if ( msg.response.exists ) { /* Handle response */
828 switch (msg.response.value) {
829 case resp_ringing:
830 rc = handle_recv_ringing ( call, &msg );
831 break;
832
833 case resp_starting:
834 rc = handle_recv_starting ( call, &msg );
835 break;
836 }
837 } else if (msg.error.exists) {
838 handle_recv_error ( call, &msg );
839 rc = -1;
840 } else {
841 LOGGER_WARNING("Invalid message; none of the request, response or error!");
842 call->error = msi_InvalidMessage;
843 rc = -1;
844 }
845
846
847 if (rc == -1) {
848 if (call->error != msi_ErrorNone)
849 send_error(m, friend_id, call->error);
850
851 kill_call(call);
852 }
853}
diff --git a/toxav/msi.h b/toxav/msi.h
index a55d8567..783d3928 100644
--- a/toxav/msi.h
+++ b/toxav/msi.h
@@ -42,6 +42,7 @@ typedef enum {
42 msi_StrayMessage, 42 msi_StrayMessage,
43 msi_SystemError, 43 msi_SystemError,
44 msi_ErrUndisclosed, 44 msi_ErrUndisclosed,
45 msi_HandleError,
45} MSIError; 46} MSIError;
46 47
47/** 48/**
@@ -70,9 +71,7 @@ typedef enum {
70 */ 71 */
71typedef enum { 72typedef enum {
72 msi_OnInvite, /* Incoming call */ 73 msi_OnInvite, /* Incoming call */
73 msi_OnRinging, /* When peer is ready to accept/reject the call */
74 msi_OnStart, /* Call (RTP transmission) started */ 74 msi_OnStart, /* Call (RTP transmission) started */
75 msi_OnReject, /* The side that was invited rejected the call */
76 msi_OnEnd, /* Call that was active ended */ 75 msi_OnEnd, /* Call that was active ended */
77 msi_OnError, /* On protocol error */ 76 msi_OnError, /* On protocol error */
78 msi_OnPeerTimeout, /* Peer timed out; stop the call */ 77 msi_OnPeerTimeout, /* Peer timed out; stop the call */
@@ -100,9 +99,12 @@ typedef struct MSICall_s {
100 99
101 100
102/** 101/**
103 * Msi callback type. 'agent' is a pointer to ToxAv 102 * Msi callback type. 'agent' is a pointer to ToxAv.
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.
104 */ 106 */
105typedef void ( *MSICallbackType ) ( void *agent, MSICall* call); 107typedef int ( *MSICallbackType ) ( void *agent, MSICall* call);
106 108
107/** 109/**
108 * Control session struct. Please do not modify outside msi.c 110 * Control session struct. Please do not modify outside msi.c
@@ -117,7 +119,7 @@ typedef struct MSISession_s {
117 Messenger *messenger; 119 Messenger *messenger;
118 120
119 pthread_mutex_t mutex[1]; 121 pthread_mutex_t mutex[1];
120 MSICallbackType callbacks[8]; 122 MSICallbackType callbacks[7];
121} MSISession; 123} MSISession;
122 124
123/** 125/**
@@ -151,11 +153,6 @@ int msi_hangup ( MSICall* call );
151int msi_answer ( MSICall* call, uint8_t capabilities ); 153int msi_answer ( MSICall* call, uint8_t capabilities );
152 154
153/** 155/**
154 * Reject incoming call. NOTE: 'call' will be freed
155 */
156int msi_reject ( MSICall* call );
157
158/**
159 * Change capabilities of the call. 156 * Change capabilities of the call.
160 */ 157 */
161int msi_change_capabilities ( MSICall* call, uint8_t capabilities ); 158int msi_change_capabilities ( MSICall* call, uint8_t capabilities );
diff --git a/toxav/rtp.c b/toxav/rtp.c
index 396e0202..8319c7dc 100644
--- a/toxav/rtp.c
+++ b/toxav/rtp.c
@@ -478,12 +478,24 @@ void rtp_kill ( RTPSession *session )
478 free ( session ); 478 free ( session );
479} 479}
480 480
481int rtp_register_for_receiving(RTPSession* session) 481int rtp_start_receiving(RTPSession* session)
482{ 482{
483 if (session == NULL)
484 return 0;
485
483 return custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, 486 return custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix,
484 rtp_handle_packet, session); 487 rtp_handle_packet, session);
485} 488}
486 489
490int rtp_stop_receiving(RTPSession* session)
491{
492 if (session == NULL)
493 return 0;
494
495 return custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix,
496 NULL, NULL);
497}
498
487int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) 499int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length )
488{ 500{
489 RTPMessage *msg = rtp_new_message (session, data, length); 501 RTPMessage *msg = rtp_new_message (session, data, length);
diff --git a/toxav/rtp.h b/toxav/rtp.h
index e3b38a8e..2950941b 100644
--- a/toxav/rtp.h
+++ b/toxav/rtp.h
@@ -119,7 +119,12 @@ void rtp_kill ( RTPSession* session );
119/** 119/**
120 * By default rtp is not in receiving state 120 * By default rtp is not in receiving state
121 */ 121 */
122int rtp_register_for_receiving (RTPSession *session); 122int rtp_start_receiving (RTPSession *session);
123
124/**
125 * Pause rtp receiving mode.
126 */
127int rtp_stop_receiving (RTPSession *session);
123 128
124/** 129/**
125 * Sends msg to RTPSession::dest 130 * Sends msg to RTPSession::dest
diff --git a/toxav/toxav.c b/toxav/toxav.c
index 584b3898..5054d399 100644
--- a/toxav/toxav.c
+++ b/toxav/toxav.c
@@ -42,6 +42,7 @@ enum {
42 42
43typedef struct ToxAVCall_s 43typedef struct ToxAVCall_s
44{ 44{
45 ToxAV* av;
45 pthread_mutex_t mutex_control[1]; 46 pthread_mutex_t mutex_control[1];
46 pthread_mutex_t mutex_encoding_audio[1]; 47 pthread_mutex_t mutex_encoding_audio[1];
47 pthread_mutex_t mutex_encoding_video[1]; 48 pthread_mutex_t mutex_encoding_video[1];
@@ -55,6 +56,8 @@ typedef struct ToxAVCall_s
55 uint32_t s_audio_b; /* Sending audio bitrate */ 56 uint32_t s_audio_b; /* Sending audio bitrate */
56 uint32_t s_video_b; /* Sending video bitrate */ 57 uint32_t s_video_b; /* Sending video bitrate */
57 58
59 uint8_t last_capabilities;
60
58 struct ToxAVCall_s *prev; 61 struct ToxAVCall_s *prev;
59 struct ToxAVCall_s *next; 62 struct ToxAVCall_s *next;
60} ToxAVCall; 63} ToxAVCall;
@@ -83,22 +86,20 @@ struct toxAV
83}; 86};
84 87
85 88
86void i_callback_invite(void* toxav_inst, MSICall* call); 89int callback_invite(void* toxav_inst, MSICall* call);
87void i_callback_ringing(void* toxav_inst, MSICall* call); 90int callback_start(void* toxav_inst, MSICall* call);
88void i_callback_start(void* toxav_inst, MSICall* call); 91int callback_end(void* toxav_inst, MSICall* call);
89void i_callback_end(void* toxav_inst, MSICall* call); 92int callback_error(void* toxav_inst, MSICall* call);
90void i_callback_error(void* toxav_inst, MSICall* call); 93int callback_capabilites(void* toxav_inst, MSICall* call);
91void i_callback_capabilites(void* toxav_inst, MSICall* call);
92 94
93TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities); 95TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities);
94ToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number); 96ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error);
95ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number); 97ToxAVCall* call_get(ToxAV* av, uint32_t friend_number);
96void i_toxav_remove_call(ToxAV* av, uint32_t friend_number); 98void call_remove(ToxAVCall* call);
97ToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); 99bool audio_bitrate_invalid(uint32_t bitrate);
98bool i_toxav_audio_bitrate_invalid(uint32_t bitrate); 100bool video_bitrate_invalid(uint32_t bitrate);
99bool i_toxav_video_bitrate_invalid(uint32_t bitrate); 101bool call_prepare_transmission(ToxAVCall* call);
100bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call); 102void call_kill_transmission(ToxAVCall* call);
101void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number);
102 103
103 104
104 105
@@ -136,14 +137,12 @@ ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error)
136 av->interval = 200; 137 av->interval = 200;
137 av->msi->av = av; 138 av->msi->av = av;
138 139
139 msi_register_callback(av->msi, i_callback_invite, msi_OnInvite); 140 msi_register_callback(av->msi, callback_invite, msi_OnInvite);
140 msi_register_callback(av->msi, i_callback_ringing, msi_OnRinging); 141 msi_register_callback(av->msi, callback_start, msi_OnStart);
141 msi_register_callback(av->msi, i_callback_start, msi_OnStart); 142 msi_register_callback(av->msi, callback_end, msi_OnEnd);
142 msi_register_callback(av->msi, i_callback_end, msi_OnReject); 143 msi_register_callback(av->msi, callback_error, msi_OnError);
143 msi_register_callback(av->msi, i_callback_end, msi_OnEnd); 144 msi_register_callback(av->msi, callback_error, msi_OnPeerTimeout);
144 msi_register_callback(av->msi, i_callback_error, msi_OnError); 145 msi_register_callback(av->msi, callback_capabilites, msi_OnCapabilities);
145 msi_register_callback(av->msi, i_callback_error, msi_OnPeerTimeout);
146 msi_register_callback(av->msi, i_callback_capabilites, msi_OnCapabilities);
147 146
148 147
149 if (error) 148 if (error)
@@ -166,7 +165,16 @@ void toxav_kill(ToxAV* av)
166 return; 165 return;
167 166
168 msi_kill(av->msi); 167 msi_kill(av->msi);
169 /* TODO iterate over calls */ 168
169 /* Msi kill will hang up all calls so just clean these calls */
170 if (av->calls) {
171 ToxAVCall* it = call_get(av, av->calls_head);
172 for (; it; it = it->next) {
173 call_kill_transmission(it);
174 call_remove(it); /* This will eventually free av->calls */
175 }
176 }
177
170 free(av); 178 free(av);
171} 179}
172 180
@@ -208,20 +216,20 @@ void toxav_iteration(ToxAV* av)
208 216
209bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) 217bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error)
210{ 218{
211 ToxAVCall* call = i_toxav_init_call(av, friend_number, error); 219 ToxAVCall* call = call_new(av, friend_number, error);
212 if (call == NULL) 220 if (call == NULL)
213 return false; 221 return false;
214 222
215 call->s_audio_b = audio_bit_rate; 223 call->s_audio_b = audio_bit_rate;
216 call->s_video_b = video_bit_rate; 224 call->s_video_b = video_bit_rate;
217 225
218 uint8_t capabilities = 0; 226 call->last_capabilities = msi_CapRAudio | msi_CapRVideo;
219 227
220 capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; 228 call->last_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
221 capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; 229 call->last_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
222 230
223 if (msi_invite(av->msi, &call->msi_call, friend_number, capabilities) != 0) { 231 if (msi_invite(av->msi, &call->msi_call, friend_number, call->last_capabilities) != 0) {
224 i_toxav_remove_call(av, friend_number); 232 call_remove(call);
225 if (error) 233 if (error)
226 *error = TOXAV_ERR_CALL_MALLOC; 234 *error = TOXAV_ERR_CALL_MALLOC;
227 return false; 235 return false;
@@ -244,14 +252,14 @@ bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, ui
244 goto END; 252 goto END;
245 } 253 }
246 254
247 if ((audio_bit_rate && i_toxav_audio_bitrate_invalid(audio_bit_rate)) 255 if ((audio_bit_rate && audio_bitrate_invalid(audio_bit_rate))
248 ||(video_bit_rate && i_toxav_video_bitrate_invalid(video_bit_rate)) 256 ||(video_bit_rate && video_bitrate_invalid(video_bit_rate))
249 ) { 257 ) {
250 rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; 258 rc = TOXAV_ERR_CALL_INVALID_BIT_RATE;
251 goto END; 259 goto END;
252 } 260 }
253 261
254 ToxAVCall* call = i_toxav_get_call(av, friend_number); 262 ToxAVCall* call = call_get(av, friend_number);
255 if (call == NULL) { 263 if (call == NULL) {
256 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; 264 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING;
257 goto END; 265 goto END;
@@ -260,12 +268,12 @@ bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, ui
260 call->s_audio_b = audio_bit_rate; 268 call->s_audio_b = audio_bit_rate;
261 call->s_video_b = video_bit_rate; 269 call->s_video_b = video_bit_rate;
262 270
263 uint8_t capabilities = 0; 271 call->last_capabilities = msi_CapRAudio | msi_CapRVideo;
264 272
265 capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; 273 call->last_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
266 capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; 274 call->last_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
267 275
268 if (msi_answer(call->msi_call, capabilities) != 0) 276 if (msi_answer(call->msi_call, call->last_capabilities) != 0)
269 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; /* the only reason for msi_answer to fail */ 277 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; /* the only reason for msi_answer to fail */
270 278
271 279
@@ -292,7 +300,7 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
292 } 300 }
293 301
294 302
295 ToxAVCall* call = i_toxav_get_call(av, friend_number); 303 ToxAVCall* call = call_get(av, friend_number);
296 if (call == NULL) { 304 if (call == NULL) {
297 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; 305 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
298 goto END; 306 goto END;
@@ -302,29 +310,53 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
302 switch (control) 310 switch (control)
303 { 311 {
304 case TOXAV_CALL_CONTROL_RESUME: { 312 case TOXAV_CALL_CONTROL_RESUME: {
305 313 if (call->msi_call->self_capabilities == 0 &&
314 call->last_capabilities ) {
315 /* Only act if paused and had media transfer active before */
316
317 if (msi_change_capabilities(call->msi_call, call->last_capabilities) == -1)
318 return false;
319
320 rtp_start_receiving(call->rtps[audio_index]);
321 rtp_start_receiving(call->rtps[video_index]);
322 }
306 } break; 323 } break;
307 324
308 case TOXAV_CALL_CONTROL_PAUSE: { 325 case TOXAV_CALL_CONTROL_PAUSE: {
309 326 if (call->msi_call->self_capabilities) {
327 /* Only act if not already paused */
328
329 call->last_capabilities = call->msi_call->self_capabilities;
330
331 if (msi_change_capabilities(call->msi_call, 0) == -1 )
332 return false;
333
334 rtp_stop_receiving(call->rtps[audio_index]);
335 rtp_stop_receiving(call->rtps[video_index]);
336 }
310 } break; 337 } break;
311 338
312 case TOXAV_CALL_CONTROL_CANCEL: { 339 case TOXAV_CALL_CONTROL_CANCEL: {
313 if (call->msi_call->state == msi_CallActive 340 /* Hang up */
314 || call->msi_call->state == msi_CallRequesting) { 341 msi_hangup(call->msi_call);
315 /* Hang up */
316 msi_hangup(call->msi_call);
317 } else if (call->msi_call->state == msi_CallRequested) {
318 /* Reject the call */
319 msi_reject(call->msi_call);
320 }
321 342
322 // No mather the case, terminate the call 343 /* No mather the case, terminate the call */
323 i_toxav_remove_call(av, friend_number); 344 call_remove(call);
324 } break; 345 } break;
325 346
326 case TOXAV_CALL_CONTROL_MUTE_AUDIO: { 347 case TOXAV_CALL_CONTROL_MUTE_AUDIO: {
327 348 if (call->msi_call->self_capabilities & msi_CapRAudio ||
349 call->msi_call->self_capabilities & msi_CapSAudio) {
350
351 uint8_t capabilities = call->msi_call->self_capabilities;
352 capabilities ^= msi_CapRAudio;
353 capabilities ^= msi_CapRAudio;
354
355 if (msi_change_capabilities(call->msi_call, call->msi_call->self_capabilities) == -1)
356 return false;
357
358 rtp_stop_receiving(call->rtps[audio_index]);
359 }
328 } break; 360 } break;
329 361
330 case TOXAV_CALL_CONTROL_MUTE_VIDEO: { 362 case TOXAV_CALL_CONTROL_MUTE_VIDEO: {
@@ -364,7 +396,7 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u
364 goto END; 396 goto END;
365 } 397 }
366 398
367 call = i_toxav_get_call(av, friend_number); 399 call = call_get(av, friend_number);
368 if (call == NULL) { 400 if (call == NULL) {
369 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; 401 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
370 goto END; 402 goto END;
@@ -461,7 +493,7 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc
461 goto END; 493 goto END;
462 } 494 }
463 495
464 call = i_toxav_get_call(av, friend_number); 496 call = call_get(av, friend_number);
465 if (call == NULL) { 497 if (call == NULL) {
466 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; 498 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
467 goto END; 499 goto END;
@@ -526,19 +558,16 @@ void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb*
526 * :: Internal 558 * :: Internal
527 * 559 *
528 ******************************************************************************/ 560 ******************************************************************************/
529/** TODO: 561/** TODO:
530 * - If crutial callback not present send error.
531 * - Error handling by return values from callbacks and setting 'error'.
532 */ 562 */
533void i_callback_invite(void* toxav_inst, MSICall* call) 563int callback_invite(void* toxav_inst, MSICall* call)
534{ 564{
535 ToxAV* toxav = toxav_inst; 565 ToxAV* toxav = toxav_inst;
536 566
537 ToxAVCall* av_call = i_toxav_init_call(toxav, call->friend_id, NULL); 567 ToxAVCall* av_call = call_new(toxav, call->friend_id, NULL);
538 if (av_call == NULL) { 568 if (av_call == NULL) {
539 LOGGER_WARNING("Failed to start call, rejecting..."); 569 LOGGER_WARNING("Failed to initialize call...");
540 msi_reject(call); 570 return -1;
541 return;
542 } 571 }
543 572
544 call->av_call = av_call; 573 call->av_call = av_call;
@@ -547,55 +576,59 @@ void i_callback_invite(void* toxav_inst, MSICall* call)
547 if (toxav->ccb.first) 576 if (toxav->ccb.first)
548 toxav->ccb.first(toxav, call->friend_id, call->peer_capabilities & msi_CapSAudio, 577 toxav->ccb.first(toxav, call->friend_id, call->peer_capabilities & msi_CapSAudio,
549 call->peer_capabilities & msi_CapSVideo, toxav->ccb.second); 578 call->peer_capabilities & msi_CapSVideo, toxav->ccb.second);
579
580 return 0;
550} 581}
551 582
552void i_callback_ringing(void* toxav_inst, MSICall* call) 583int callback_start(void* toxav_inst, MSICall* call)
553{
554 ToxAV* toxav = toxav_inst;
555 if (toxav->scb.first)
556 toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_RINGING, toxav->scb.second);
557}
558
559void i_callback_start(void* toxav_inst, MSICall* call)
560{ 584{
561 ToxAV* toxav = toxav_inst; 585 ToxAV* toxav = toxav_inst;
562 586
563 ToxAVCall* av_call = i_toxav_get_call(toxav, call->friend_id); 587 ToxAVCall* av_call = call_get(toxav, call->friend_id);
564 588
565 if (av_call == NULL || !i_toxav_prepare_transmission(toxav, av_call)) { 589 if (av_call == NULL || !call_prepare_transmission(av_call)) {
566 /* TODO send error */ 590 call_remove(av_call);
567 i_toxav_remove_call(toxav, call->friend_id); 591 return -1;
568 return;
569 } 592 }
570 593
571 TOXAV_CALL_STATE state = capabilities_to_state(av_call->msi_call->peer_capabilities); 594 TOXAV_CALL_STATE state = capabilities_to_state(av_call->msi_call->peer_capabilities);
572 595
573 if (toxav->scb.first) 596 if (toxav->scb.first)
574 toxav->scb.first(toxav, call->friend_id, state, toxav->scb.second); 597 toxav->scb.first(toxav, call->friend_id, state, toxav->scb.second);
598
599 return 0;
575} 600}
576 601
577void i_callback_end(void* toxav_inst, MSICall* call) 602int callback_end(void* toxav_inst, MSICall* call)
578{ 603{
579 ToxAV* toxav = toxav_inst; 604 ToxAV* toxav = toxav_inst;
580 605
581 i_toxav_kill_transmission(toxav, call->friend_id); 606 call_kill_transmission(call->av_call);
582 i_toxav_remove_call(toxav, call->friend_id); 607 call_remove(call->av_call);
583 608
584 if (toxav->scb.first) 609 if (toxav->scb.first)
585 toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_END, toxav->scb.second); 610 toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_END, toxav->scb.second);
611
612 return 0;
586} 613}
587 614
588void i_callback_error(void* toxav_inst, MSICall* call) 615int callback_error(void* toxav_inst, MSICall* call)
589{ 616{
590 ToxAV* toxav = toxav_inst; 617 ToxAV* toxav = toxav_inst;
591 if (toxav->scb.first) 618 if (toxav->scb.first)
592 toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR, toxav->scb.second); 619 toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR, toxav->scb.second);
620
621 return 0;
593} 622}
594 623
595void i_callback_capabilites(void* toxav_inst, MSICall* call) 624int callback_capabilites(void* toxav_inst, MSICall* call)
596{ 625{
597 ToxAV* toxav = toxav_inst; 626 ToxAV* toxav = toxav_inst;
598 /* TODO handle this */ 627 if (toxav->scb.first)
628 toxav->scb.first(toxav, call->friend_id,
629 capabilities_to_state(call->peer_capabilities), toxav->scb.second);
630
631 return 0;
599} 632}
600 633
601TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities) 634TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities)
@@ -610,32 +643,64 @@ TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities)
610 return TOXAV_CALL_STATE_PAUSED; 643 return TOXAV_CALL_STATE_PAUSED;
611} 644}
612 645
613ToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number) 646bool audio_bitrate_invalid(uint32_t bitrate)
614{ 647{
615 if (av->calls == NULL || av->calls_tail < friend_number) 648 /* Opus RFC 6716 section-2.1.1 dictates the following:
616 return NULL; 649 * Opus supports all bitrates from 6 kbit/s to 510 kbit/s.
617 650 */
618 return av->calls[friend_number]; 651 return bitrate < 6 || bitrate > 510;
619} 652}
620 653
621ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number) 654bool video_bitrate_invalid(uint32_t bitrate)
622{ 655{
623 ToxAVCall* rc = calloc(sizeof(ToxAVCall), 1); 656 /* TODO: If anyone knows the answer to this one please fill it up */
657 return false;
658}
659
660ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
661{
662 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
663 ToxAVCall* call = NULL;
624 664
625 if (rc == NULL) 665 if (m_friend_exists(av->m, friend_number) == 0) {
626 return NULL; 666 rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND;
667 goto END;
668 }
627 669
628 rc->friend_id = friend_number; 670 if (m_get_friend_connectionstatus(av->m, friend_number) != 1) {
671 rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
672 goto END;
673 }
629 674
630 if (create_recursive_mutex(rc->mutex_control) != 0) { 675 if (call_get(av, friend_number) != NULL) {
631 free(rc); 676 rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL;
632 return NULL; 677 goto END;
633 } 678 }
634 679
635 if (create_recursive_mutex(rc->mutex_do) != 0) { 680
636 pthread_mutex_destroy(rc->mutex_control); 681 call = calloc(sizeof(ToxAVCall), 1);
637 free(rc); 682
638 return NULL; 683 if (call == NULL) {
684 rc = TOXAV_ERR_CALL_MALLOC;
685 goto END;
686 }
687
688 call->av = av;
689 call->friend_id = friend_number;
690
691 if (create_recursive_mutex(call->mutex_control) != 0) {
692 free(call);
693 call = NULL;
694 rc = TOXAV_ERR_CALL_MALLOC;
695 goto END;
696 }
697
698 if (create_recursive_mutex(call->mutex_do) != 0) {
699 pthread_mutex_destroy(call->mutex_control);
700 free(call);
701 call = NULL;
702 rc = TOXAV_ERR_CALL_MALLOC;
703 goto END;
639 } 704 }
640 705
641 706
@@ -643,10 +708,12 @@ ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number)
643 av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1); 708 av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1);
644 709
645 if (av->calls == NULL) { 710 if (av->calls == NULL) {
646 pthread_mutex_destroy(rc->mutex_control); 711 pthread_mutex_destroy(call->mutex_control);
647 pthread_mutex_destroy(rc->mutex_do); 712 pthread_mutex_destroy(call->mutex_do);
648 free(rc); 713 free(call);
649 return NULL; 714 call = NULL;
715 rc = TOXAV_ERR_CALL_MALLOC;
716 goto END;
650 } 717 }
651 718
652 av->calls_tail = av->calls_head = friend_number; 719 av->calls_tail = av->calls_head = friend_number;
@@ -655,10 +722,12 @@ ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number)
655 void* tmp = realloc(av->calls, sizeof(ToxAVCall*) * friend_number + 1); 722 void* tmp = realloc(av->calls, sizeof(ToxAVCall*) * friend_number + 1);
656 723
657 if (tmp == NULL) { 724 if (tmp == NULL) {
658 pthread_mutex_destroy(rc->mutex_control); 725 pthread_mutex_destroy(call->mutex_control);
659 pthread_mutex_destroy(rc->mutex_do); 726 pthread_mutex_destroy(call->mutex_do);
660 free(rc); 727 free(call);
661 return NULL; 728 call = NULL;
729 rc = TOXAV_ERR_CALL_MALLOC;
730 goto END;
662 } 731 }
663 732
664 av->calls = tmp; 733 av->calls = tmp;
@@ -668,105 +737,41 @@ ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number)
668 for (; i < friend_number; i ++) 737 for (; i < friend_number; i ++)
669 av->calls[i] = NULL; 738 av->calls[i] = NULL;
670 739
671 rc->prev = av->calls[av->calls_tail]; 740 call->prev = av->calls[av->calls_tail];
672 av->calls[av->calls_tail]->next = rc; 741 av->calls[av->calls_tail]->next = call;
673 742
674 av->calls_tail = friend_number; 743 av->calls_tail = friend_number;
675 744
676 } else if (av->calls_head > friend_number) { /* Inserting at front */ 745 } else if (av->calls_head > friend_number) { /* Inserting at front */
677 rc->next = av->calls[av->calls_head]; 746 call->next = av->calls[av->calls_head];
678 av->calls[av->calls_head]->prev = rc; 747 av->calls[av->calls_head]->prev = call;
679 av->calls_head = friend_number; 748 av->calls_head = friend_number;
680 } 749 }
681 750
682 av->calls[friend_number] = rc; 751 av->calls[friend_number] = call;
683 return rc;
684}
685
686void i_toxav_remove_call(ToxAV* av, uint32_t friend_number)
687{
688 ToxAVCall* tc = i_toxav_get_call(av, friend_number);
689
690 if (tc == NULL)
691 return;
692
693 ToxAVCall* prev = tc->prev;
694 ToxAVCall* next = tc->next;
695
696 pthread_mutex_destroy(tc->mutex_control);
697 pthread_mutex_destroy(tc->mutex_do);
698
699 free(tc);
700
701 if (prev)
702 prev->next = next;
703 else if (next)
704 av->calls_head = next->friend_id;
705 else goto CLEAR;
706 752
707 if (next) 753END:
708 next->prev = prev;
709 else if (prev)
710 av->calls_tail = prev->friend_id;
711 else goto CLEAR;
712
713 av->calls[friend_number] = NULL;
714 return;
715
716CLEAR:
717 av->calls_head = av->calls_tail = 0;
718 free(av->calls);
719 av->calls = NULL;
720}
721
722ToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
723{
724 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
725 ToxAVCall* call = NULL;
726
727 if (m_friend_exists(av->m, friend_number) == 0) {
728 rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND;
729 goto END;
730 }
731
732 if (m_get_friend_connectionstatus(av->m, friend_number) != 1) {
733 rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
734 goto END;
735 }
736
737 if (i_toxav_get_call(av, friend_number) != NULL) {
738 rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL;
739 goto END;
740 }
741
742 call = i_toxav_add_call(av, friend_number);
743 if (call == NULL) {
744 rc = TOXAV_ERR_CALL_MALLOC;
745 }
746
747 END:
748 if (error) 754 if (error)
749 *error = rc; 755 *error = rc;
750 756
751 return call; 757 return call;
752} 758}
753 759
754bool i_toxav_audio_bitrate_invalid(uint32_t bitrate) 760ToxAVCall* call_get(ToxAV* av, uint32_t friend_number)
755{ 761{
756 /* Opus RFC 6716 section-2.1.1 dictates the following: 762 if (av->calls == NULL || av->calls_tail < friend_number)
757 * Opus supports all bitrates from 6 kbit/s to 510 kbit/s. 763 return NULL;
758 */ 764
759 return bitrate < 6 || bitrate > 510; 765 return av->calls[friend_number];
760}
761
762bool i_toxav_video_bitrate_invalid(uint32_t bitrate)
763{
764 /* TODO: If anyone knows the answer to this one please fill it up */
765 return false;
766} 766}
767 767
768bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call) 768bool call_prepare_transmission(ToxAVCall* call)
769{ 769{
770 if (call == NULL)
771 return false;
772
773 ToxAV* av = call->av;
774
770 if (!av->acb.first && !av->vcb.first) 775 if (!av->acb.first && !av->vcb.first)
771 /* It makes no sense to have CSession without callbacks */ 776 /* It makes no sense to have CSession without callbacks */
772 return false; 777 return false;
@@ -830,7 +835,7 @@ bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call)
830 goto FAILURE; 835 goto FAILURE;
831 } 836 }
832 837
833 rtp_register_for_receiving(call->rtps[audio_index]); 838 rtp_start_receiving(call->rtps[audio_index]);
834 } 839 }
835 } 840 }
836 841
@@ -854,8 +859,8 @@ bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call)
854 LOGGER_WARNING("Failed to enable video receiving!"); 859 LOGGER_WARNING("Failed to enable video receiving!");
855 goto FAILURE; 860 goto FAILURE;
856 } 861 }
857 862
858 rtp_register_for_receiving(call->rtps[audio_index]); 863 rtp_start_receiving(call->rtps[audio_index]);
859 } 864 }
860 } 865 }
861 866
@@ -863,7 +868,7 @@ bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call)
863 pthread_mutex_unlock(call->mutex_control); 868 pthread_mutex_unlock(call->mutex_control);
864 return true; 869 return true;
865 870
866FAILURE: 871 FAILURE:
867 rtp_kill(call->rtps[audio_index]); 872 rtp_kill(call->rtps[audio_index]);
868 call->rtps[audio_index] = NULL; 873 call->rtps[audio_index] = NULL;
869 rtp_kill(call->rtps[video_index]); 874 rtp_kill(call->rtps[video_index]);
@@ -878,25 +883,19 @@ FAILURE:
878 pthread_mutex_unlock(call->mutex_control); 883 pthread_mutex_unlock(call->mutex_control);
879 return false; 884 return false;
880 885
881MUTEX_INIT_ERROR: 886 MUTEX_INIT_ERROR:
882 pthread_mutex_unlock(call->mutex_control); 887 pthread_mutex_unlock(call->mutex_control);
883 LOGGER_ERROR("Mutex initialization failed!\n"); 888 LOGGER_ERROR("Mutex initialization failed!\n");
884 return false; 889 return false;
885} 890}
886 891
887void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number) 892void call_kill_transmission(ToxAVCall* call)
888{ 893{
889 ToxAVCall* call = i_toxav_get_call(av, friend_number); 894 if (call == NULL || call->active == 0)
890 if (!call)
891 return; 895 return;
892 896
893 pthread_mutex_lock(call->mutex_control); 897 pthread_mutex_lock(call->mutex_control);
894 898
895 if (!call->active) {
896 pthread_mutex_unlock(call->mutex_control);
897 return;
898 }
899
900 call->active = 0; 899 call->active = 0;
901 900
902 pthread_mutex_lock(call->mutex_encoding_audio); 901 pthread_mutex_lock(call->mutex_encoding_audio);
@@ -918,4 +917,41 @@ void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number)
918 pthread_mutex_destroy(call->mutex_do); 917 pthread_mutex_destroy(call->mutex_do);
919 918
920 pthread_mutex_unlock(call->mutex_control); 919 pthread_mutex_unlock(call->mutex_control);
920}
921
922void call_remove(ToxAVCall* call)
923{
924 if (call == NULL)
925 return;
926
927 uint32_t friend_id = call->friend_id;
928 ToxAV* av = call->av;
929
930 ToxAVCall* prev = call->prev;
931 ToxAVCall* next = call->next;
932
933 pthread_mutex_destroy(call->mutex_control);
934 pthread_mutex_destroy(call->mutex_do);
935
936 free(call);
937
938 if (prev)
939 prev->next = next;
940 else if (next)
941 av->calls_head = next->friend_id;
942 else goto CLEAR;
943
944 if (next)
945 next->prev = prev;
946 else if (prev)
947 av->calls_tail = prev->friend_id;
948 else goto CLEAR;
949
950 av->calls[friend_id] = NULL;
951 return;
952
953CLEAR:
954 av->calls_head = av->calls_tail = 0;
955 free(av->calls);
956 av->calls = NULL;
921} \ No newline at end of file 957} \ No newline at end of file
diff --git a/toxav/toxav.h b/toxav/toxav.h
index c1c6019c..5e82faa6 100644
--- a/toxav/toxav.h
+++ b/toxav/toxav.h
@@ -187,11 +187,6 @@ bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, ui
187 ******************************************************************************/ 187 ******************************************************************************/
188typedef enum TOXAV_CALL_STATE { 188typedef enum TOXAV_CALL_STATE {
189 /** 189 /**
190 * The friend's client is aware of the call. This happens after calling
191 * toxav_call and the initial call request has been received.
192 */
193 TOXAV_CALL_STATE_RINGING,
194 /**
195 * Not sending anything. Either the friend requested that this client stops 190 * Not sending anything. Either the friend requested that this client stops
196 * sending anything, or the client turned off both audio and video by setting 191 * sending anything, or the client turned off both audio and video by setting
197 * the respective bit rates to 0. 192 * the respective bit rates to 0.