summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toxav/av_test.c53
-rw-r--r--toxav/codec.c30
-rw-r--r--toxav/msi.c111
-rw-r--r--toxav/msi.h19
-rw-r--r--toxav/toxav.c300
-rw-r--r--toxav/toxav.h66
6 files changed, 334 insertions, 245 deletions
diff --git a/toxav/av_test.c b/toxav/av_test.c
index 41f5a758..bb79eedc 100644
--- a/toxav/av_test.c
+++ b/toxav/av_test.c
@@ -16,7 +16,7 @@
16 16
17typedef struct { 17typedef struct {
18 bool incoming; 18 bool incoming;
19 TOXAV_CALL_STATE state; 19 uint32_t state;
20} CallControl; 20} CallControl;
21 21
22const char* stringify_state(TOXAV_CALL_STATE s) 22const char* stringify_state(TOXAV_CALL_STATE s)
@@ -44,9 +44,9 @@ void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool
44 printf("Handling CALL callback\n"); 44 printf("Handling CALL callback\n");
45 ((CallControl*)user_data)->incoming = true; 45 ((CallControl*)user_data)->incoming = true;
46} 46}
47void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, TOXAV_CALL_STATE state, void *user_data) 47void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
48{ 48{
49 printf("Handling CALL STATE callback: %s\n", stringify_state(state)); 49 printf("Handling CALL STATE callback: %d\n", state);
50 50
51 ((CallControl*)user_data)->state = state; 51 ((CallControl*)user_data)->state = state;
52} 52}
@@ -211,13 +211,13 @@ int main (int argc, char** argv)
211 } 211 }
212 212
213 printf("\nTrying regular call (Audio and Video)...\n"); 213 printf("\nTrying regular call (Audio and Video)...\n");
214// REGULAR_CALL_FLOW(48, 4000); 214 REGULAR_CALL_FLOW(48, 4000);
215 215
216 printf("\nTrying regular call (Audio only)...\n"); 216 printf("\nTrying regular call (Audio only)...\n");
217// REGULAR_CALL_FLOW(48, 0); 217 REGULAR_CALL_FLOW(48, 0);
218 218
219 printf("\nTrying regular call (Video only)...\n"); 219 printf("\nTrying regular call (Video only)...\n");
220// REGULAR_CALL_FLOW(0, 4000); 220 REGULAR_CALL_FLOW(0, 4000);
221 221
222#undef REGULAR_CALL_FLOW 222#undef REGULAR_CALL_FLOW
223 223
@@ -318,10 +318,8 @@ int main (int argc, char** argv)
318 /* At first try all stuff while in invalid state */ 318 /* At first try all stuff while in invalid state */
319 assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL)); 319 assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL));
320 assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL)); 320 assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
321 assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL)); 321 assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL));
322 assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_VIDEO, NULL)); 322 assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_VIDEO, NULL));
323 assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL));
324 assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_VIDEO, NULL));
325 323
326 { 324 {
327 TOXAV_ERR_ANSWER rc; 325 TOXAV_ERR_ANSWER rc;
@@ -342,36 +340,36 @@ int main (int argc, char** argv)
342 assert(BobCC.state == TOXAV_CALL_STATE_PAUSED); 340 assert(BobCC.state == TOXAV_CALL_STATE_PAUSED);
343 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL)); 341 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
344 iterate(Bsn, AliceAV, BobAV); 342 iterate(Bsn, AliceAV, BobAV);
345 assert(BobCC.state == TOXAV_CALL_STATE_SENDING_AV); 343 assert(BobCC.state & (TOXAV_CALL_STATE_SENDING_A | TOXAV_CALL_STATE_SENDING_V));
346 344
347 /* Mute/Unmute single */ 345 /* Mute/Unmute single */
348 printf("Mute/Unmute single\n"); 346 printf("Mute/Unmute single\n");
349 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL)); 347 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL));
350 iterate(Bsn, AliceAV, BobAV); 348 iterate(Bsn, AliceAV, BobAV);
351 assert(BobCC.state == TOXAV_CALL_CONTROL_MUTE_AUDIO); 349 assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_A);
352 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL)); 350 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL));
353 iterate(Bsn, AliceAV, BobAV); 351 iterate(Bsn, AliceAV, BobAV);
354 assert(BobCC.state == TOXAV_CALL_CONTROL_UNMUTE_AUDIO); 352 assert(BobCC.state & TOXAV_CALL_STATE_RECEIVING_A);
355 353
356 /* Mute/Unmute both */ 354 /* Mute/Unmute both */
357 printf("Mute/Unmute both\n"); 355 printf("Mute/Unmute both\n");
358 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL)); 356 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL));
359 iterate(Bsn, AliceAV, BobAV); 357 iterate(Bsn, AliceAV, BobAV);
360 assert(BobCC.state == TOXAV_CALL_STATE_SENDING_V); 358 assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_A);
361 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_VIDEO, NULL)); 359 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_VIDEO, NULL));
362 iterate(Bsn, AliceAV, BobAV); 360 iterate(Bsn, AliceAV, BobAV);
363 assert(BobCC.state == TOXAV_CALL_STATE_NOT_SENDING); 361 assert(BobCC.state ^ TOXAV_CALL_STATE_RECEIVING_V);
364 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL)); 362 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO, NULL));
365 iterate(Bsn, AliceAV, BobAV); 363 iterate(Bsn, AliceAV, BobAV);
366 assert(BobCC.state == TOXAV_CALL_STATE_SENDING_A); 364 assert(BobCC.state & TOXAV_CALL_STATE_RECEIVING_A);
367 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_VIDEO, NULL)); 365 assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_TOGGLE_MUTE_VIDEO, NULL));
368 iterate(Bsn, AliceAV, BobAV); 366 iterate(Bsn, AliceAV, BobAV);
369 assert(BobCC.state == TOXAV_CALL_STATE_SENDING_AV); 367 assert(BobCC.state & TOXAV_CALL_STATE_RECEIVING_V);
370 368
371 { 369 {
372 TOXAV_ERR_CALL_CONTROL rc; 370 TOXAV_ERR_CALL_CONTROL rc;
373 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); 371 toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
374 372
375 if (rc != TOXAV_ERR_CALL_CONTROL_OK) { 373 if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
376 printf("toxav_call_control failed: %d\n", rc); 374 printf("toxav_call_control failed: %d\n", rc);
377 exit(1); 375 exit(1);
@@ -384,6 +382,13 @@ int main (int argc, char** argv)
384 printf("Success!\n"); 382 printf("Success!\n");
385 } 383 }
386 384
385
386 toxav_kill(BobAV);
387 toxav_kill(AliceAV);
388 tox_kill(Bob);
389 tox_kill(Alice);
390 tox_kill(Bsn);
391
387 printf("\nTest successful!\n"); 392 printf("\nTest successful!\n");
388 return 0; 393 return 0;
389} \ No newline at end of file 394} \ No newline at end of file
diff --git a/toxav/codec.c b/toxav/codec.c
index e44387df..645f7188 100644
--- a/toxav/codec.c
+++ b/toxav/codec.c
@@ -343,21 +343,17 @@ CSSession *cs_new(uint32_t peer_video_frame_piece_size)
343 return NULL; 343 return NULL;
344 } 344 }
345 345
346 cs->peer_video_frame_piece_size = peer_video_frame_piece_size;
347
348 return cs;
349 346
350 FAILURE: 347 if (create_recursive_mutex(cs->queue_mutex) != 0) {
351 LOGGER_WARNING("Error initializing codec session! Application might misbehave!"); 348 LOGGER_WARNING("Failed to create recursive mutex!");
349 free(cs);
350 return NULL;
351 }
352 352
353 cs_disable_audio_sending(cs);
354 cs_disable_audio_receiving(cs);
355 cs_disable_video_sending(cs);
356 cs_disable_video_receiving(cs);
357 353
358 free(cs); 354 cs->peer_video_frame_piece_size = peer_video_frame_piece_size;
359 355
360 return NULL; 356 return cs;
361} 357}
362 358
363void cs_kill(CSSession *cs) 359void cs_kill(CSSession *cs)
@@ -374,6 +370,8 @@ void cs_kill(CSSession *cs)
374 cs_disable_video_sending(cs); 370 cs_disable_video_sending(cs);
375 cs_disable_video_receiving(cs); 371 cs_disable_video_receiving(cs);
376 372
373 pthread_mutex_destroy(cs->queue_mutex);
374
377 LOGGER_DEBUG("Terminated codec state: %p", cs); 375 LOGGER_DEBUG("Terminated codec state: %p", cs);
378 free(cs); 376 free(cs);
379} 377}
@@ -536,19 +534,12 @@ int cs_enable_video_receiving(CSSession* cs)
536{ 534{
537 if (cs->v_decoding) 535 if (cs->v_decoding)
538 return 0; 536 return 0;
539 537
540 if (create_recursive_mutex(cs->queue_mutex) != 0) {
541 LOGGER_WARNING("Failed to create recursive mutex!");
542 return -1;
543 }
544
545 int rc = vpx_codec_dec_init_ver(cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, 538 int rc = vpx_codec_dec_init_ver(cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE,
546 NULL, 0, VPX_DECODER_ABI_VERSION); 539 NULL, 0, VPX_DECODER_ABI_VERSION);
547 540
548 if ( rc != VPX_CODEC_OK) { 541 if ( rc != VPX_CODEC_OK) {
549 LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); 542 LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
550
551 pthread_mutex_destroy(cs->queue_mutex);
552 return -1; 543 return -1;
553 } 544 }
554 545
@@ -591,7 +582,6 @@ void cs_disable_video_receiving(CSSession* cs)
591 cs->frame_buf = NULL; 582 cs->frame_buf = NULL;
592 583
593 vpx_codec_destroy(cs->v_decoder); 584 vpx_codec_destroy(cs->v_decoder);
594 pthread_mutex_destroy(cs->queue_mutex);
595 } 585 }
596} 586}
597 587
diff --git a/toxav/msi.c b/toxav/msi.c
index f179a7ab..ac900dac 100644
--- a/toxav/msi.c
+++ b/toxav/msi.c
@@ -99,7 +99,9 @@ void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint1
99 */ 99 */
100void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id) 100void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id)
101{ 101{
102 pthread_mutex_lock(session->mutex);
102 session->callbacks[id] = callback; 103 session->callbacks[id] = callback;
104 pthread_mutex_unlock(session->mutex);
103} 105}
104MSISession *msi_new ( Messenger *messenger ) 106MSISession *msi_new ( Messenger *messenger )
105{ 107{
@@ -163,15 +165,19 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_
163{ 165{
164 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id); 166 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id);
165 167
168 pthread_mutex_lock(session->mutex);
166 if (get_call(session, friend_id) != NULL) { 169 if (get_call(session, friend_id) != NULL) {
167 LOGGER_ERROR("Already in a call"); 170 LOGGER_ERROR("Already in a call");
171 pthread_mutex_unlock(session->mutex);
168 return -1; 172 return -1;
169 } 173 }
170 174
171 (*call) = new_call ( session, friend_id ); 175 (*call) = new_call ( session, friend_id );
172 176
173 if ( *call == NULL ) 177 if ( *call == NULL ) {
178 pthread_mutex_unlock(session->mutex);
174 return -1; 179 return -1;
180 }
175 181
176 (*call)->self_capabilities = capabilities; 182 (*call)->self_capabilities = capabilities;
177 183
@@ -180,7 +186,7 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_
180 186
181 msg.capabilities.exists = true; 187 msg.capabilities.exists = true;
182 msg.capabilities.value = capabilities; 188 msg.capabilities.value = capabilities;
183 189
184 msg.vfpsz.exists = true; 190 msg.vfpsz.exists = true;
185 msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE); 191 msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE);
186 192
@@ -189,28 +195,37 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_
189 (*call)->state = msi_CallRequesting; 195 (*call)->state = msi_CallRequesting;
190 196
191 LOGGER_DEBUG("Invite sent"); 197 LOGGER_DEBUG("Invite sent");
198 pthread_mutex_unlock(session->mutex);
192 return 0; 199 return 0;
193} 200}
194int msi_hangup ( MSICall* call ) 201int msi_hangup ( MSICall* call )
195{ 202{
196 LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_id); 203 LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_id);
197 204
205 MSISession* session = call->session;
206 pthread_mutex_lock(session->mutex);
207
198 MSIMessage msg; 208 MSIMessage msg;
199 msg_init(&msg, requ_pop); 209 msg_init(&msg, requ_pop);
200 210
201 send_message ( call->session->messenger, call->friend_id, &msg ); 211 send_message ( session->messenger, call->friend_id, &msg );
202 212
203 kill_call(call); 213 kill_call(call);
214 pthread_mutex_unlock(session->mutex);
204 return 0; 215 return 0;
205} 216}
206int msi_answer ( MSICall* call, uint8_t capabilities ) 217int msi_answer ( MSICall* call, uint8_t capabilities )
207{ 218{
208 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_id); 219 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_id);
209 220
221 MSISession* session = call->session;
222 pthread_mutex_lock(session->mutex);
223
210 if ( call->state != msi_CallRequested ) { 224 if ( call->state != msi_CallRequested ) {
211 /* Though sending in invalid state will not cause anything wierd 225 /* Though sending in invalid state will not cause anything wierd
212 * Its better to not do it like a maniac */ 226 * Its better to not do it like a maniac */
213 LOGGER_ERROR("Call is in invalid state!"); 227 LOGGER_ERROR("Call is in invalid state!");
228 pthread_mutex_unlock(session->mutex);
214 return -1; 229 return -1;
215 } 230 }
216 231
@@ -225,9 +240,10 @@ int msi_answer ( MSICall* call, uint8_t capabilities )
225 msg.vfpsz.exists = true; 240 msg.vfpsz.exists = true;
226 msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE); 241 msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE);
227 242
228 send_message ( call->session->messenger, call->friend_id, &msg ); 243 send_message ( session->messenger, call->friend_id, &msg );
229 244
230 call->state = msi_CallActive; 245 call->state = msi_CallActive;
246 pthread_mutex_unlock(session->mutex);
231 247
232 return 0; 248 return 0;
233} 249}
@@ -235,6 +251,9 @@ int msi_change_capabilities( MSICall* call, uint8_t capabilities )
235{ 251{
236 LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_id); 252 LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_id);
237 253
254 MSISession* session = call->session;
255 pthread_mutex_lock(session->mutex);
256
238 if ( call->state != msi_CallActive ) { 257 if ( call->state != msi_CallActive ) {
239 /* Sending capabilities change can cause error on other side if 258 /* Sending capabilities change can cause error on other side if
240 * the call is not active since we don't send header 'vfpsz'. 259 * the call is not active since we don't send header 'vfpsz'.
@@ -244,6 +263,7 @@ int msi_change_capabilities( MSICall* call, uint8_t capabilities )
244 * like new. TODO: explain this better 263 * like new. TODO: explain this better
245 */ 264 */
246 LOGGER_ERROR("Call is in invalid state!"); 265 LOGGER_ERROR("Call is in invalid state!");
266 pthread_mutex_unlock(session->mutex);
247 return -1; 267 return -1;
248 } 268 }
249 269
@@ -257,6 +277,7 @@ int msi_change_capabilities( MSICall* call, uint8_t capabilities )
257 277
258 send_message ( call->session->messenger, call->friend_id, &msg ); 278 send_message ( call->session->messenger, call->friend_id, &msg );
259 279
280 pthread_mutex_unlock(session->mutex);
260 return 0; 281 return 0;
261} 282}
262 283
@@ -316,7 +337,7 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
316 337
317 case IDError: 338 case IDError:
318 CHECK_SIZE(it, size_constraint, 1); 339 CHECK_SIZE(it, size_constraint, 1);
319 CHECK_ENUM_HIGH(it, msi_ErrUndisclosed); 340 CHECK_ENUM_HIGH(it, msi_EUndisclosed);
320 SET_UINT8(it, dest->error); 341 SET_UINT8(it, dest->error);
321 break; 342 break;
322 343
@@ -440,7 +461,7 @@ int invoke_callback(MSICall* call, MSICallbackID cb)
440 if ( call->session->callbacks[cb] ) { 461 if ( call->session->callbacks[cb] ) {
441 LOGGER_DEBUG("Invoking callback function: %d", cb); 462 LOGGER_DEBUG("Invoking callback function: %d", cb);
442 if ( call->session->callbacks[cb] ( call->session->av, call ) != 0 ) { 463 if ( call->session->callbacks[cb] ( call->session->av, call ) != 0 ) {
443 LOGGER_WARNING("Callback handling failed, sending error"); 464 LOGGER_WARNING("Callback state handling failed, sending error");
444 goto FAILURE; 465 goto FAILURE;
445 } 466 }
446 467
@@ -449,10 +470,11 @@ int invoke_callback(MSICall* call, MSICallbackID cb)
449 470
450FAILURE: 471FAILURE:
451 /* If no callback present or error happened while handling, 472 /* If no callback present or error happened while handling,
452 * an error message will be send to friend 473 * an error message will be sent to friend
453 */ 474 */
454 475
455 call->error = msi_HandleError; 476 if (call->error == msi_ENone)
477 call->error = msi_EHandle;
456 return -1; 478 return -1;
457} 479}
458static MSICall *get_call ( MSISession *session, uint32_t friend_id ) 480static MSICall *get_call ( MSISession *session, uint32_t friend_id )
@@ -517,9 +539,12 @@ MSICall *new_call ( MSISession *session, uint32_t friend_id )
517} 539}
518void kill_call ( MSICall *call ) 540void kill_call ( MSICall *call )
519{ 541{
542 /* Assume that session mutex is locked */
520 if ( call == NULL ) 543 if ( call == NULL )
521 return; 544 return;
522 545
546 LOGGER_DEBUG("Killing call: %p", call);
547
523 MSISession* session = call->session; 548 MSISession* session = call->session;
524 549
525 MSICall* prev = call->prev; 550 MSICall* prev = call->prev;
@@ -529,23 +554,23 @@ void kill_call ( MSICall *call )
529 prev->next = next; 554 prev->next = next;
530 else if (next) 555 else if (next)
531 session->calls_head = next->friend_id; 556 session->calls_head = next->friend_id;
532 else goto CLEAR; 557 else goto CLEAR_CONTAINER;
533 558
534 if (next) 559 if (next)
535 next->prev = prev; 560 next->prev = prev;
536 else if (prev) 561 else if (prev)
537 session->calls_tail = prev->friend_id; 562 session->calls_tail = prev->friend_id;
538 else goto CLEAR; 563 else goto CLEAR_CONTAINER;
539 564
540 session->calls[call->friend_id] = NULL; 565 session->calls[call->friend_id] = NULL;
541 free(call); 566 free(call);
542 return; 567 return;
543 568
544CLEAR: 569CLEAR_CONTAINER:
545 session->calls_head = session->calls_tail = 0; 570 session->calls_head = session->calls_tail = 0;
546 free(session->calls); 571 free(session->calls);
547 session->calls = NULL;
548 free(call); 572 free(call);
573 session->calls = NULL;
549} 574}
550void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data) 575void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data)
551{ 576{
@@ -556,13 +581,17 @@ void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data)
556 case 0: { /* Friend is now offline */ 581 case 0: { /* Friend is now offline */
557 LOGGER_DEBUG("Friend %d is now offline", friend_id); 582 LOGGER_DEBUG("Friend %d is now offline", friend_id);
558 583
584 pthread_mutex_lock(session->mutex);
559 MSICall* call = get_call(session, friend_id); 585 MSICall* call = get_call(session, friend_id);
560 586
561 if (call == NULL) 587 if (call == NULL) {
588 pthread_mutex_unlock(session->mutex);
562 return; 589 return;
590 }
563 591
564 invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */ 592 invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */
565 kill_call(call); 593 kill_call(call);
594 pthread_mutex_unlock(session->mutex);
566 } 595 }
567 break; 596 break;
568 597
@@ -580,18 +609,17 @@ void handle_push ( MSICall *call, const MSIMessage *msg )
580 609
581 if (!msg->capabilities.exists) { 610 if (!msg->capabilities.exists) {
582 LOGGER_WARNING("Session: %p Invalid capabilities on 'push'"); 611 LOGGER_WARNING("Session: %p Invalid capabilities on 'push'");
583 call->error = msi_InvalidMessage; 612 call->error = msi_EInvalidMessage;
584 goto FAILURE; 613 goto FAILURE;
585 } 614 }
586 615
587 if (call->state != msi_CallActive) { 616 if (call->state != msi_CallActive) {
588 if (!msg->vfpsz.exists) { 617 if (!msg->vfpsz.exists) {
589 LOGGER_WARNING("Session: %p Invalid vfpsz on 'push'"); 618 LOGGER_WARNING("Session: %p Invalid vfpsz on 'push'");
590 call->error = msi_InvalidMessage; 619 call->error = msi_EInvalidMessage;
591 goto FAILURE; 620 goto FAILURE;
592 } 621 }
593 622
594 /* Sending video frame piece size is ignored when call is active */
595 call->peer_vfpsz = ntohs(msg->vfpsz.value); 623 call->peer_vfpsz = ntohs(msg->vfpsz.value);
596 } 624 }
597 625
@@ -610,6 +638,38 @@ void handle_push ( MSICall *call, const MSIMessage *msg )
610 } break; 638 } break;
611 639
612 case msi_CallActive: { 640 case msi_CallActive: {
641 if (msg->vfpsz.exists) {
642 /* If peer sended video frame piece size
643 * while the call is already active it's probable
644 * that he is trying to re-call us while the call
645 * is not terminated on our side. We can assume that
646 * in this case we can automatically answer the re-call.
647 */
648 if (call->peer_vfpsz != ntohs(msg->vfpsz.value)) {
649 LOGGER_WARNING("Friend sent invalid parameters for re-call");
650 call->error = msi_EInvalidParam;
651 invoke_callback(call, msi_OnError);
652 goto FAILURE;
653 }
654
655 LOGGER_INFO("Friend is recalling us");
656
657 MSIMessage msg;
658 msg_init(&msg, requ_push);
659
660 msg.capabilities.exists = true;
661 msg.capabilities.value = call->self_capabilities;
662
663 msg.vfpsz.exists = true;
664 msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE);
665
666 send_message ( call->session->messenger, call->friend_id, &msg );
667
668 /* If peer changed capabilities during re-call they will
669 * be handled accordingly during the next step
670 */
671 }
672
613 /* Only act if capabilities changed */ 673 /* Only act if capabilities changed */
614 if ( call->peer_capabilities != msg->capabilities.value) { 674 if ( call->peer_capabilities != msg->capabilities.value) {
615 LOGGER_INFO("Friend is changing capabilities"); 675 LOGGER_INFO("Friend is changing capabilities");
@@ -629,6 +689,7 @@ void handle_push ( MSICall *call, const MSIMessage *msg )
629 689
630 if ( invoke_callback(call, msi_OnStart) == -1 ) 690 if ( invoke_callback(call, msi_OnStart) == -1 )
631 goto FAILURE; 691 goto FAILURE;
692
632 } break; 693 } break;
633 694
634 case msi_CallRequested: { 695 case msi_CallRequested: {
@@ -646,7 +707,7 @@ FAILURE:
646void handle_pop ( MSICall *call, const MSIMessage *msg ) 707void handle_pop ( MSICall *call, const MSIMessage *msg )
647{ 708{
648 assert(call); 709 assert(call);
649 710
650 LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_id); 711 LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_id);
651 712
652 /* callback errors are ignored */ 713 /* callback errors are ignored */
@@ -682,8 +743,6 @@ void handle_pop ( MSICall *call, const MSIMessage *msg )
682 } 743 }
683 744
684 kill_call ( call ); 745 kill_call ( call );
685
686 return;
687} 746}
688void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object ) 747void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object )
689{ 748{
@@ -694,30 +753,34 @@ void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint1
694 753
695 if ( msg_parse_in ( &msg, data, length ) == -1 ) { 754 if ( msg_parse_in ( &msg, data, length ) == -1 ) {
696 LOGGER_WARNING("Error parsing message"); 755 LOGGER_WARNING("Error parsing message");
697 send_error(m, friend_id, msi_InvalidMessage); 756 send_error(m, friend_id, msi_EInvalidMessage);
698 return; 757 return;
699 } else { 758 } else {
700 LOGGER_DEBUG("Successfully parsed message"); 759 LOGGER_DEBUG("Successfully parsed message");
701 } 760 }
702 761
762 pthread_mutex_lock(session->mutex);
703 MSICall *call = get_call(session, friend_id); 763 MSICall *call = get_call(session, friend_id);
704 764
705 if (call == NULL) { 765 if (call == NULL) {
706 if (msg.request.value != requ_push) { 766 if (msg.request.value != requ_push) {
707 send_error(m, friend_id, msi_StrayMessage); 767 send_error(m, friend_id, msi_EStrayMessage);
768 pthread_mutex_unlock(session->mutex);
708 return; 769 return;
709 } 770 }
710 771
711 call = new_call(session, friend_id); 772 call = new_call(session, friend_id);
712 if (call == NULL) { 773 if (call == NULL) {
713 send_error(m, friend_id, msi_SystemError); 774 send_error(m, friend_id, msi_ESystem);
775 pthread_mutex_unlock(session->mutex);
714 return; 776 return;
715 } 777 }
716 } 778 }
717 779
718 780 if (msg.request.value == requ_push)
719 if (msg.request.value == requ_push)
720 handle_push(call, &msg); 781 handle_push(call, &msg);
721 else 782 else
722 handle_pop(call, &msg); /* always kills the call */ 783 handle_pop(call, &msg); /* always kills the call */
784
785 pthread_mutex_unlock(session->mutex);
723} \ No newline at end of file 786} \ No newline at end of file
diff --git a/toxav/msi.h b/toxav/msi.h
index b846542d..8404df19 100644
--- a/toxav/msi.h
+++ b/toxav/msi.h
@@ -35,14 +35,14 @@
35 * Error codes. 35 * Error codes.
36 */ 36 */
37typedef enum { 37typedef enum {
38 msi_ErrorNone, 38 msi_ENone,
39 msi_InvalidMessage, 39 msi_EInvalidMessage,
40 msi_InvalidParam, 40 msi_EInvalidParam,
41 msi_InvalidState, 41 msi_EInvalidState,
42 msi_StrayMessage, 42 msi_EStrayMessage,
43 msi_SystemError, 43 msi_ESystem,
44 msi_HandleError, 44 msi_EHandle,
45 msi_ErrUndisclosed, /* NOTE: must be last enum otherwise parsing wont work */ 45 msi_EUndisclosed, /* NOTE: must be last enum otherwise parsing wont work */
46} MSIError; 46} MSIError;
47 47
48/** 48/**
@@ -118,6 +118,9 @@ typedef struct MSISession_s {
118 void *av; 118 void *av;
119 Messenger *messenger; 119 Messenger *messenger;
120 120
121 /* The mutex controls async access from control
122 * thread(s) and core thread.
123 */
121 pthread_mutex_t mutex[1]; 124 pthread_mutex_t mutex[1];
122 MSICallbackType callbacks[7]; 125 MSICallbackType callbacks[7];
123} MSISession; 126} MSISession;
diff --git a/toxav/toxav.c b/toxav/toxav.c
index e6b51ee4..78243ae6 100644
--- a/toxav/toxav.c
+++ b/toxav/toxav.c
@@ -40,15 +40,16 @@ enum {
40 video_index, 40 video_index,
41}; 41};
42 42
43typedef struct ToxAVCall_s 43typedef struct ToxAVCall_s {
44{
45 ToxAV* av; 44 ToxAV* av;
46 pthread_mutex_t mutex_control[1];
47 pthread_mutex_t mutex_encoding_audio[1];
48 pthread_mutex_t mutex_encoding_video[1];
49 pthread_mutex_t mutex_do[1];
50 RTPSession *rtps[2]; /* Audio is first and video is second */ 45 RTPSession *rtps[2]; /* Audio is first and video is second */
51 CSSession *cs; 46 CSSession *cs;
47
48 pthread_mutex_t mutex_audio_sending[1];
49 pthread_mutex_t mutex_video_sending[1];
50 /* Only audio or video can be decoded at one time */
51 pthread_mutex_t mutex_decoding[1];
52
52 bool active; 53 bool active;
53 MSICall* msi_call; 54 MSICall* msi_call;
54 uint32_t friend_id; 55 uint32_t friend_id;
@@ -56,14 +57,14 @@ typedef struct ToxAVCall_s
56 uint32_t s_audio_b; /* Sending audio bitrate */ 57 uint32_t s_audio_b; /* Sending audio bitrate */
57 uint32_t s_video_b; /* Sending video bitrate */ 58 uint32_t s_video_b; /* Sending video bitrate */
58 59
59 uint8_t last_capabilities; 60 uint8_t last_self_capabilities;
61 uint8_t last_peer_capabilities;
60 62
61 struct ToxAVCall_s *prev; 63 struct ToxAVCall_s *prev;
62 struct ToxAVCall_s *next; 64 struct ToxAVCall_s *next;
63} ToxAVCall; 65} ToxAVCall;
64 66
65struct toxAV 67struct toxAV {
66{
67 Messenger* m; 68 Messenger* m;
68 MSISession* msi; 69 MSISession* msi;
69 70
@@ -71,11 +72,14 @@ struct toxAV
71 ToxAVCall** calls; 72 ToxAVCall** calls;
72 uint32_t calls_tail; 73 uint32_t calls_tail;
73 uint32_t calls_head; 74 uint32_t calls_head;
75 pthread_mutex_t mutex[1];
74 76
75 PAIR(toxav_call_cb *, void*) ccb; /* Call callback */ 77 PAIR(toxav_call_cb *, void*) ccb; /* Call callback */
76 PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */ 78 PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */
77 PAIR(toxav_receive_audio_frame_cb *, void *) acb; /* Audio frame receive callback */ 79 PAIR(toxav_receive_audio_frame_cb *, void *) acb; /* Audio frame receive callback */
78 PAIR(toxav_receive_video_frame_cb *, void *) vcb; /* Video frame receive callback */ 80 PAIR(toxav_receive_video_frame_cb *, void *) vcb; /* Video frame receive callback */
81 PAIR(toxav_request_video_frame_cb *, void *) rvcb; /* Request video callback */
82 PAIR(toxav_request_audio_frame_cb *, void *) racb; /* Request video callback */
79 83
80 /** Decode time measures */ 84 /** Decode time measures */
81 int32_t dmssc; /** Measure count */ 85 int32_t dmssc; /** Measure count */
@@ -92,7 +96,6 @@ int callback_end(void* toxav_inst, MSICall* call);
92int callback_error(void* toxav_inst, MSICall* call); 96int callback_error(void* toxav_inst, MSICall* call);
93int callback_capabilites(void* toxav_inst, MSICall* call); 97int callback_capabilites(void* toxav_inst, MSICall* call);
94 98
95TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities);
96ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); 99ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error);
97ToxAVCall* call_get(ToxAV* av, uint32_t friend_number); 100ToxAVCall* call_get(ToxAV* av, uint32_t friend_number);
98void call_remove(ToxAVCall* call); 101void call_remove(ToxAVCall* call);
@@ -118,7 +121,7 @@ ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error)
118 goto FAILURE; 121 goto FAILURE;
119 } 122 }
120 123
121 av = calloc ( sizeof(ToxAV), 1); 124 av = calloc (sizeof(ToxAV), 1);
122 125
123 if (av == NULL) { 126 if (av == NULL) {
124 LOGGER_WARNING("Allocation failed!"); 127 LOGGER_WARNING("Allocation failed!");
@@ -126,10 +129,17 @@ ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error)
126 goto FAILURE; 129 goto FAILURE;
127 } 130 }
128 131
132 if (create_recursive_mutex(av->mutex) == -1) {
133 LOGGER_WARNING("Mutex creation failed!");
134 rc = TOXAV_ERR_NEW_MALLOC;
135 goto FAILURE;
136 }
137
129 av->m = (Messenger *)tox; 138 av->m = (Messenger *)tox;
130 av->msi = msi_new(av->m); 139 av->msi = msi_new(av->m);
131 140
132 if (av->msi == NULL) { 141 if (av->msi == NULL) {
142 pthread_mutex_destroy(av->mutex);
133 rc = TOXAV_ERR_NEW_MALLOC; 143 rc = TOXAV_ERR_NEW_MALLOC;
134 goto FAILURE; 144 goto FAILURE;
135 } 145 }
@@ -163,6 +173,7 @@ void toxav_kill(ToxAV* av)
163{ 173{
164 if (av == NULL) 174 if (av == NULL)
165 return; 175 return;
176 pthread_mutex_lock(av->mutex);
166 177
167 msi_kill(av->msi); 178 msi_kill(av->msi);
168 179
@@ -175,6 +186,8 @@ void toxav_kill(ToxAV* av)
175 } 186 }
176 } 187 }
177 188
189 pthread_mutex_unlock(av->mutex);
190 pthread_mutex_destroy(av->mutex);
178 free(av); 191 free(av);
179} 192}
180 193
@@ -185,6 +198,7 @@ Tox* toxav_get_tox(ToxAV* av)
185 198
186uint32_t toxav_iteration_interval(const ToxAV* av) 199uint32_t toxav_iteration_interval(const ToxAV* av)
187{ 200{
201 /* If no call is active interval is 200 */
188 return av->calls ? av->interval : 200; 202 return av->calls ? av->interval : 200;
189} 203}
190 204
@@ -194,14 +208,28 @@ void toxav_iteration(ToxAV* av)
194 return; 208 return;
195 209
196 uint64_t start = current_time_monotonic(); 210 uint64_t start = current_time_monotonic();
197 uint32_t rc = 200 + av->dmssa; /* If no call is active interval is 200 */ 211 uint32_t rc = 0;
198 212
213 pthread_mutex_lock(av->mutex);
199 ToxAVCall* i = av->calls[av->calls_head]; 214 ToxAVCall* i = av->calls[av->calls_head];
200 for (; i; i = i->next) { 215 for (; i; i = i->next) {
201 if (i->active) { 216 if (i->active) {
217 pthread_mutex_lock(i->mutex_decoding);
218
219 /* TODO make AV synchronisation */
220 if (av->racb.first)
221 av->racb.first(av, i->friend_id, av->racb.second);
222 if (av->rvcb.first)
223 av->rvcb.first(av, i->friend_id, av->rvcb.second);
224
225 pthread_mutex_unlock(av->mutex);
202 cs_do(i->cs); 226 cs_do(i->cs);
203 rc = MIN(i->cs->last_packet_frame_duration, rc); 227 rc = MIN(i->cs->last_packet_frame_duration, rc);
204 } 228 pthread_mutex_unlock(i->mutex_decoding);
229 } else
230 continue;
231
232 pthread_mutex_lock(av->mutex);
205 } 233 }
206 234
207 av->interval = rc < av->dmssa ? 0 : rc - av->dmssa; 235 av->interval = rc < av->dmssa ? 0 : rc - av->dmssa;
@@ -216,38 +244,47 @@ void toxav_iteration(ToxAV* av)
216 244
217bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) 245bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error)
218{ 246{
247 pthread_mutex_lock(av->mutex);
219 ToxAVCall* call = call_new(av, friend_number, error); 248 ToxAVCall* call = call_new(av, friend_number, error);
220 if (call == NULL) 249 if (call == NULL) {
250 pthread_mutex_unlock(av->mutex);
221 return false; 251 return false;
252 }
222 253
223 call->s_audio_b = audio_bit_rate; 254 call->s_audio_b = audio_bit_rate;
224 call->s_video_b = video_bit_rate; 255 call->s_video_b = video_bit_rate;
225 256
226 call->last_capabilities = msi_CapRAudio | msi_CapRVideo; 257 call->last_self_capabilities = msi_CapRAudio | msi_CapRVideo;
227 258
228 call->last_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; 259 call->last_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
229 call->last_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; 260 call->last_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
230 261
231 if (msi_invite(av->msi, &call->msi_call, friend_number, call->last_capabilities) != 0) { 262 if (msi_invite(av->msi, &call->msi_call, friend_number, call->last_self_capabilities) != 0) {
232 call_remove(call); 263 call_remove(call);
233 if (error) 264 if (error)
234 *error = TOXAV_ERR_CALL_MALLOC; 265 *error = TOXAV_ERR_CALL_MALLOC;
266 pthread_mutex_unlock(av->mutex);
235 return false; 267 return false;
236 } 268 }
237 269
238 call->msi_call->av_call = call; 270 call->msi_call->av_call = call;
271 pthread_mutex_unlock(av->mutex);
239 272
240 return true; 273 return true;
241} 274}
242 275
243void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data) 276void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data)
244{ 277{
278 pthread_mutex_lock(av->mutex);
245 av->ccb.first = function; 279 av->ccb.first = function;
246 av->ccb.second = user_data; 280 av->ccb.second = user_data;
281 pthread_mutex_unlock(av->mutex);
247} 282}
248 283
249bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error) 284bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error)
250{ 285{
286 pthread_mutex_lock(av->mutex);
287
251 TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK; 288 TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK;
252 if (m_friend_exists(av->m, friend_number) == 0) { 289 if (m_friend_exists(av->m, friend_number) == 0) {
253 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND; 290 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND;
@@ -270,16 +307,18 @@ bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, ui
270 call->s_audio_b = audio_bit_rate; 307 call->s_audio_b = audio_bit_rate;
271 call->s_video_b = video_bit_rate; 308 call->s_video_b = video_bit_rate;
272 309
273 call->last_capabilities = msi_CapRAudio | msi_CapRVideo; 310 call->last_self_capabilities = msi_CapRAudio | msi_CapRVideo;
274 311
275 call->last_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; 312 call->last_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
276 call->last_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; 313 call->last_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
277 314
278 if (msi_answer(call->msi_call, call->last_capabilities) != 0) 315 if (msi_answer(call->msi_call, call->last_self_capabilities) != 0)
279 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; /* the only reason for msi_answer to fail */ 316 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; /* the only reason for msi_answer to fail */
280 317
281 318
282END: 319END:
320 pthread_mutex_unlock(av->mutex);
321
283 if (error) 322 if (error)
284 *error = rc; 323 *error = rc;
285 324
@@ -288,12 +327,15 @@ END:
288 327
289void toxav_callback_call_state(ToxAV* av, toxav_call_state_cb* function, void* user_data) 328void toxav_callback_call_state(ToxAV* av, toxav_call_state_cb* function, void* user_data)
290{ 329{
330 pthread_mutex_lock(av->mutex);
291 av->scb.first = function; 331 av->scb.first = function;
292 av->scb.second = user_data; 332 av->scb.second = user_data;
333 pthread_mutex_unlock(av->mutex);
293} 334}
294 335
295bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error) 336bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error)
296{ 337{
338 pthread_mutex_lock(av->mutex);
297 TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK; 339 TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK;
298 340
299 if (m_friend_exists(av->m, friend_number) == 0) { 341 if (m_friend_exists(av->m, friend_number) == 0) {
@@ -308,9 +350,7 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
308 goto END; 350 goto END;
309 } 351 }
310 352
311 /* TODO rest of these */ 353 switch (control) {
312 switch (control)
313 {
314 case TOXAV_CALL_CONTROL_RESUME: { 354 case TOXAV_CALL_CONTROL_RESUME: {
315 if (!call->active) { 355 if (!call->active) {
316 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; 356 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
@@ -319,10 +359,10 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
319 359
320 /* Only act if paused and had media transfer active before */ 360 /* Only act if paused and had media transfer active before */
321 if (call->msi_call->self_capabilities == 0 && 361 if (call->msi_call->self_capabilities == 0 &&
322 call->last_capabilities ) { 362 call->last_self_capabilities ) {
323 363
324 if (msi_change_capabilities(call->msi_call, 364 if (msi_change_capabilities(call->msi_call,
325 call->last_capabilities) == -1) { 365 call->last_self_capabilities) == -1) {
326 /* The only reason for this function to fail is invalid state 366 /* The only reason for this function to fail is invalid state
327 * ( not active ) */ 367 * ( not active ) */
328 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; 368 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
@@ -342,7 +382,7 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
342 382
343 /* Only act if not already paused */ 383 /* Only act if not already paused */
344 if (call->msi_call->self_capabilities) { 384 if (call->msi_call->self_capabilities) {
345 call->last_capabilities = call->msi_call->self_capabilities; 385 call->last_self_capabilities = call->msi_call->self_capabilities;
346 386
347 if (msi_change_capabilities(call->msi_call, 0) == -1 ) { 387 if (msi_change_capabilities(call->msi_call, 0) == -1 ) {
348 /* The only reason for this function to fail is invalid state 388 /* The only reason for this function to fail is invalid state
@@ -365,7 +405,7 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
365 call_remove(call); 405 call_remove(call);
366 } break; 406 } break;
367 407
368 case TOXAV_CALL_CONTROL_MUTE_AUDIO: { 408 case TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO: {
369 if (!call->active) { 409 if (!call->active) {
370 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; 410 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
371 goto END; 411 goto END;
@@ -381,54 +421,42 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
381 } 421 }
382 422
383 rtp_stop_receiving(call->rtps[audio_index]); 423 rtp_stop_receiving(call->rtps[audio_index]);
384 } 424 } else {
385 } break; 425 /* This call was already muted so notify the friend that he can
386 426 * start sending audio again
387 case TOXAV_CALL_CONTROL_MUTE_VIDEO: { 427 */
388 if (!call->active) {
389 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
390 goto END;
391 }
392
393 if (call->msi_call->self_capabilities & msi_CapRVideo) {
394 if (msi_change_capabilities(call->msi_call, call-> 428 if (msi_change_capabilities(call->msi_call, call->
395 msi_call->self_capabilities ^ msi_CapRVideo) == -1) { 429 msi_call->self_capabilities | msi_CapRAudio) == -1) {
396 /* The only reason for this function to fail is invalid state 430 /* The only reason for this function to fail is invalid state
397 * ( not active ) */ 431 * ( not active ) */
398 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; 432 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
399 goto END; 433 goto END;
400 } 434 }
401 435
402 rtp_stop_receiving(call->rtps[video_index]); 436 rtp_start_receiving(call->rtps[audio_index]);
403 } 437 }
404 } break; 438 } break;
405 439
406 case TOXAV_CALL_CONTROL_UNMUTE_AUDIO: { 440 case TOXAV_CALL_CONTROL_TOGGLE_MUTE_VIDEO: {
407 if (!call->active) { 441 if (!call->active) {
408 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; 442 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
409 goto END; 443 goto END;
410 } 444 }
411 445
412 if (call->msi_call->self_capabilities & ~msi_CapRAudio) { 446 if (call->msi_call->self_capabilities & msi_CapRVideo) {
413 if (msi_change_capabilities(call->msi_call, call-> 447 if (msi_change_capabilities(call->msi_call, call->
414 msi_call->self_capabilities | msi_CapRAudio) == -1) { 448 msi_call->self_capabilities ^ msi_CapRVideo) == -1) {
415 /* The only reason for this function to fail is invalid state 449 /* The only reason for this function to fail is invalid state
416 * ( not active ) */ 450 * ( not active ) */
417 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; 451 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
418 goto END; 452 goto END;
419 } 453 }
420 454
421 rtp_start_receiving(call->rtps[audio_index]); 455 rtp_stop_receiving(call->rtps[video_index]);
422 } 456 } else {
423 } break; 457 /* This call was already muted so notify the friend that he can
424 458 * start sending video again
425 case TOXAV_CALL_CONTROL_UNMUTE_VIDEO: { 459 */
426 if (!call->active) {
427 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
428 goto END;
429 }
430
431 if (call->msi_call->self_capabilities & ~msi_CapRVideo) {
432 if (msi_change_capabilities(call->msi_call, call-> 460 if (msi_change_capabilities(call->msi_call, call->
433 msi_call->self_capabilities | msi_CapRVideo) == -1) { 461 msi_call->self_capabilities | msi_CapRVideo) == -1) {
434 /* The only reason for this function to fail is invalid state 462 /* The only reason for this function to fail is invalid state
@@ -443,6 +471,8 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
443 } 471 }
444 472
445END: 473END:
474 pthread_mutex_unlock(av->mutex);
475
446 if (error) 476 if (error)
447 *error = rc; 477 *error = rc;
448 478
@@ -461,7 +491,10 @@ bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_
461 491
462void toxav_callback_request_video_frame(ToxAV* av, toxav_request_video_frame_cb* function, void* user_data) 492void toxav_callback_request_video_frame(ToxAV* av, toxav_request_video_frame_cb* function, void* user_data)
463{ 493{
464 /* TODO */ 494 pthread_mutex_lock(av->mutex);
495 av->rvcb.first = function;
496 av->rvcb.second = user_data;
497 pthread_mutex_unlock(av->mutex);
465} 498}
466 499
467bool toxav_send_video_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) 500bool toxav_send_video_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)
@@ -474,24 +507,32 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u
474 goto END; 507 goto END;
475 } 508 }
476 509
510 pthread_mutex_lock(av->mutex);
477 call = call_get(av, friend_number); 511 call = call_get(av, friend_number);
478 if (call == NULL) { 512 if (call == NULL || !call->active) {
513 pthread_mutex_unlock(av->mutex);
479 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; 514 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
480 goto END; 515 goto END;
481 } 516 }
482 517
518 pthread_mutex_lock(call->mutex_video_sending);
519 pthread_mutex_unlock(av->mutex);
520
483 if (call->msi_call->state != msi_CallActive) { 521 if (call->msi_call->state != msi_CallActive) {
484 /* TODO */ 522 /* TODO */
523 pthread_mutex_unlock(call->mutex_video_sending);
485 rc = TOXAV_ERR_SEND_FRAME_NOT_REQUESTED; 524 rc = TOXAV_ERR_SEND_FRAME_NOT_REQUESTED;
486 goto END; 525 goto END;
487 } 526 }
488 527
489 if ( y == NULL || u == NULL || v == NULL ) { 528 if ( y == NULL || u == NULL || v == NULL ) {
529 pthread_mutex_unlock(call->mutex_video_sending);
490 rc = TOXAV_ERR_SEND_FRAME_NULL; 530 rc = TOXAV_ERR_SEND_FRAME_NULL;
491 goto END; 531 goto END;
492 } 532 }
493 533
494 if ( cs_set_sending_video_resolution(call->cs, width, height) != 0 ) { 534 if ( cs_set_sending_video_resolution(call->cs, width, height) != 0 ) {
535 pthread_mutex_unlock(call->mutex_video_sending);
495 rc = TOXAV_ERR_SEND_FRAME_INVALID; 536 rc = TOXAV_ERR_SEND_FRAME_INVALID;
496 goto END; 537 goto END;
497 } 538 }
@@ -513,6 +554,7 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u
513 554
514 vpx_img_free(&img); /* FIXME don't free? */ 555 vpx_img_free(&img); /* FIXME don't free? */
515 if ( vrc != VPX_CODEC_OK) { 556 if ( vrc != VPX_CODEC_OK) {
557 pthread_mutex_unlock(call->mutex_video_sending);
516 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc)); 558 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc));
517 rc = TOXAV_ERR_SEND_FRAME_INVALID; 559 rc = TOXAV_ERR_SEND_FRAME_INVALID;
518 goto END; 560 goto END;
@@ -542,13 +584,18 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u
542 for (i = 0; i < parts; i++) { 584 for (i = 0; i < parts; i++) {
543 iter = cs_iterate_split_video_frame(call->cs, &part_size); 585 iter = cs_iterate_split_video_frame(call->cs, &part_size);
544 586
545 if (rtp_send_msg(call->rtps[video_index], iter, part_size) < 0) 587 if (rtp_send_msg(call->rtps[video_index], iter, part_size) < 0) {
588 pthread_mutex_unlock(call->mutex_video_sending);
589 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
546 goto END; 590 goto END;
591 }
547 } 592 }
548 } 593 }
549 } 594 }
550 } 595 }
551 596
597 pthread_mutex_unlock(call->mutex_video_sending);
598
552END: 599END:
553 if (error) 600 if (error)
554 *error = rc; 601 *error = rc;
@@ -558,7 +605,10 @@ END:
558 605
559void toxav_callback_request_audio_frame(ToxAV* av, toxav_request_audio_frame_cb* function, void* user_data) 606void toxav_callback_request_audio_frame(ToxAV* av, toxav_request_audio_frame_cb* function, void* user_data)
560{ 607{
561 /* TODO */ 608 pthread_mutex_lock(av->mutex);
609 av->racb.first = function;
610 av->racb.second = user_data;
611 pthread_mutex_unlock(av->mutex);
562} 612}
563 613
564bool toxav_send_audio_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) 614bool toxav_send_audio_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)
@@ -571,24 +621,32 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc
571 goto END; 621 goto END;
572 } 622 }
573 623
624 pthread_mutex_lock(av->mutex);
574 call = call_get(av, friend_number); 625 call = call_get(av, friend_number);
575 if (call == NULL) { 626 if (call == NULL || !call->active) {
627 pthread_mutex_unlock(av->mutex);
576 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; 628 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
577 goto END; 629 goto END;
578 } 630 }
579 631
632 pthread_mutex_lock(call->mutex_audio_sending);
633 pthread_mutex_unlock(av->mutex);
634
580 if (call->msi_call->state != msi_CallActive) { 635 if (call->msi_call->state != msi_CallActive) {
581 /* TODO */ 636 /* TODO */
637 pthread_mutex_unlock(call->mutex_audio_sending);
582 rc = TOXAV_ERR_SEND_FRAME_NOT_REQUESTED; 638 rc = TOXAV_ERR_SEND_FRAME_NOT_REQUESTED;
583 goto END; 639 goto END;
584 } 640 }
585 641
586 if ( pcm == NULL ) { 642 if ( pcm == NULL ) {
643 pthread_mutex_unlock(call->mutex_audio_sending);
587 rc = TOXAV_ERR_SEND_FRAME_NULL; 644 rc = TOXAV_ERR_SEND_FRAME_NULL;
588 goto END; 645 goto END;
589 } 646 }
590 647
591 if ( channels != 1 || channels != 2 ) { 648 if ( channels != 1 || channels != 2 ) {
649 pthread_mutex_unlock(call->mutex_audio_sending);
592 rc = TOXAV_ERR_SEND_FRAME_INVALID; 650 rc = TOXAV_ERR_SEND_FRAME_INVALID;
593 goto END; 651 goto END;
594 } 652 }
@@ -604,6 +662,7 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc
604 if (vrc < 0) { 662 if (vrc < 0) {
605 LOGGER_WARNING("Failed to encode frame"); 663 LOGGER_WARNING("Failed to encode frame");
606 rc = TOXAV_ERR_SEND_FRAME_INVALID; 664 rc = TOXAV_ERR_SEND_FRAME_INVALID;
665 pthread_mutex_unlock(call->mutex_audio_sending);
607 goto END; 666 goto END;
608 } 667 }
609 668
@@ -611,6 +670,8 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc
611 /* TODO check for error? */ 670 /* TODO check for error? */
612 } 671 }
613 672
673 pthread_mutex_unlock(call->mutex_audio_sending);
674
614END: 675END:
615 if (error) 676 if (error)
616 *error = rc; 677 *error = rc;
@@ -620,14 +681,18 @@ END:
620 681
621void toxav_callback_receive_video_frame(ToxAV* av, toxav_receive_video_frame_cb* function, void* user_data) 682void toxav_callback_receive_video_frame(ToxAV* av, toxav_receive_video_frame_cb* function, void* user_data)
622{ 683{
684 pthread_mutex_lock(av->mutex);
623 av->vcb.first = function; 685 av->vcb.first = function;
624 av->vcb.second = user_data; 686 av->vcb.second = user_data;
687 pthread_mutex_unlock(av->mutex);
625} 688}
626 689
627void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb* function, void* user_data) 690void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb* function, void* user_data)
628{ 691{
692 pthread_mutex_lock(av->mutex);
629 av->acb.first = function; 693 av->acb.first = function;
630 av->acb.second = user_data; 694 av->acb.second = user_data;
695 pthread_mutex_unlock(av->mutex);
631} 696}
632 697
633 698
@@ -639,10 +704,12 @@ void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb*
639int callback_invite(void* toxav_inst, MSICall* call) 704int callback_invite(void* toxav_inst, MSICall* call)
640{ 705{
641 ToxAV* toxav = toxav_inst; 706 ToxAV* toxav = toxav_inst;
707 pthread_mutex_lock(toxav->mutex);
642 708
643 ToxAVCall* av_call = call_new(toxav, call->friend_id, NULL); 709 ToxAVCall* av_call = call_new(toxav, call->friend_id, NULL);
644 if (av_call == NULL) { 710 if (av_call == NULL) {
645 LOGGER_WARNING("Failed to initialize call..."); 711 LOGGER_WARNING("Failed to initialize call...");
712 pthread_mutex_unlock(toxav->mutex);
646 return -1; 713 return -1;
647 } 714 }
648 715
@@ -653,36 +720,41 @@ int callback_invite(void* toxav_inst, MSICall* call)
653 toxav->ccb.first(toxav, call->friend_id, call->peer_capabilities & msi_CapSAudio, 720 toxav->ccb.first(toxav, call->friend_id, call->peer_capabilities & msi_CapSAudio,
654 call->peer_capabilities & msi_CapSVideo, toxav->ccb.second); 721 call->peer_capabilities & msi_CapSVideo, toxav->ccb.second);
655 722
723 pthread_mutex_unlock(toxav->mutex);
656 return 0; 724 return 0;
657} 725}
658 726
659int callback_start(void* toxav_inst, MSICall* call) 727int callback_start(void* toxav_inst, MSICall* call)
660{ 728{
661 ToxAV* toxav = toxav_inst; 729 ToxAV* toxav = toxav_inst;
730 pthread_mutex_lock(toxav->mutex);
662 731
663 ToxAVCall* av_call = call_get(toxav, call->friend_id); 732 ToxAVCall* av_call = call_get(toxav, call->friend_id);
664 733
665 if (av_call == NULL) { 734 if (av_call == NULL) {
666 /* Should this ever happen? */ 735 /* Should this ever happen? */
736 pthread_mutex_unlock(toxav->mutex);
667 return -1; 737 return -1;
668 } 738 }
669 739
670 if (!call_prepare_transmission(av_call)) { 740 if (!call_prepare_transmission(av_call)) {
671 callback_error(toxav_inst, call); 741 callback_error(toxav_inst, call);
672 call_remove(av_call); 742 call_remove(av_call);
743 pthread_mutex_unlock(toxav->mutex);
673 return -1; 744 return -1;
674 } 745 }
675 746
676 if (toxav->scb.first) 747 if (toxav->scb.first)
677 toxav->scb.first(toxav, call->friend_id, 748 toxav->scb.first(toxav, call->friend_id, call->peer_capabilities, toxav->scb.second);
678 capabilities_to_state(call->peer_capabilities), toxav->scb.second);
679 749
750 pthread_mutex_unlock(toxav->mutex);
680 return 0; 751 return 0;
681} 752}
682 753
683int callback_end(void* toxav_inst, MSICall* call) 754int callback_end(void* toxav_inst, MSICall* call)
684{ 755{
685 ToxAV* toxav = toxav_inst; 756 ToxAV* toxav = toxav_inst;
757 pthread_mutex_lock(toxav->mutex);
686 758
687 if (toxav->scb.first) 759 if (toxav->scb.first)
688 toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_END, toxav->scb.second); 760 toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_END, toxav->scb.second);
@@ -690,43 +762,39 @@ int callback_end(void* toxav_inst, MSICall* call)
690 call_kill_transmission(call->av_call); 762 call_kill_transmission(call->av_call);
691 call_remove(call->av_call); 763 call_remove(call->av_call);
692 764
765 pthread_mutex_unlock(toxav->mutex);
693 return 0; 766 return 0;
694} 767}
695 768
696int callback_error(void* toxav_inst, MSICall* call) 769int callback_error(void* toxav_inst, MSICall* call)
697{ 770{
698 ToxAV* toxav = toxav_inst; 771 ToxAV* toxav = toxav_inst;
772 pthread_mutex_lock(toxav->mutex);
699 773
700 if (toxav->scb.first) 774 if (toxav->scb.first)
701 toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR, toxav->scb.second); 775 toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR, toxav->scb.second);
702 776
777 call_kill_transmission(call->av_call);
778 call_remove(call->av_call);
779
780 pthread_mutex_unlock(toxav->mutex);
703 return 0; 781 return 0;
704} 782}
705 783
706int callback_capabilites(void* toxav_inst, MSICall* call) 784int callback_capabilites(void* toxav_inst, MSICall* call)
707{ 785{
708 ToxAV* toxav = toxav_inst; 786 ToxAV* toxav = toxav_inst;
787 pthread_mutex_lock(toxav->mutex);
788
789 /* TODO modify cs? */
790
709 if (toxav->scb.first) 791 if (toxav->scb.first)
710 toxav->scb.first(toxav, call->friend_id, 792 toxav->scb.first(toxav, call->friend_id, call->peer_capabilities, toxav->scb.second);
711 capabilities_to_state(call->peer_capabilities), toxav->scb.second);
712 793
794 pthread_mutex_unlock(toxav->mutex);
713 return 0; 795 return 0;
714} 796}
715 797
716TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities)
717{
718 if (capabilities == 0)
719 return TOXAV_CALL_STATE_PAUSED;
720 else if ((capabilities & msi_CapSAudio) && (capabilities & msi_CapSVideo))
721 return TOXAV_CALL_STATE_SENDING_AV;
722 else if (capabilities & msi_CapSAudio)
723 return TOXAV_CALL_STATE_SENDING_A;
724 else if (capabilities & msi_CapSVideo)
725 return TOXAV_CALL_STATE_SENDING_V;
726
727 return TOXAV_CALL_STATE_NOT_SENDING;
728}
729
730bool audio_bitrate_invalid(uint32_t bitrate) 798bool audio_bitrate_invalid(uint32_t bitrate)
731{ 799{
732 /* Opus RFC 6716 section-2.1.1 dictates the following: 800 /* Opus RFC 6716 section-2.1.1 dictates the following:
@@ -743,6 +811,7 @@ bool video_bitrate_invalid(uint32_t bitrate)
743 811
744ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error) 812ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
745{ 813{
814 /* Assumes mutex locked */
746 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK; 815 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
747 ToxAVCall* call = NULL; 816 ToxAVCall* call = NULL;
748 817
@@ -772,28 +841,10 @@ ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
772 call->av = av; 841 call->av = av;
773 call->friend_id = friend_number; 842 call->friend_id = friend_number;
774 843
775 if (create_recursive_mutex(call->mutex_control) != 0) {
776 free(call);
777 call = NULL;
778 rc = TOXAV_ERR_CALL_MALLOC;
779 goto END;
780 }
781
782 if (create_recursive_mutex(call->mutex_do) != 0) {
783 pthread_mutex_destroy(call->mutex_control);
784 free(call);
785 call = NULL;
786 rc = TOXAV_ERR_CALL_MALLOC;
787 goto END;
788 }
789
790
791 if (av->calls == NULL) { /* Creating */ 844 if (av->calls == NULL) { /* Creating */
792 av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1); 845 av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1);
793 846
794 if (av->calls == NULL) { 847 if (av->calls == NULL) {
795 pthread_mutex_destroy(call->mutex_control);
796 pthread_mutex_destroy(call->mutex_do);
797 free(call); 848 free(call);
798 call = NULL; 849 call = NULL;
799 rc = TOXAV_ERR_CALL_MALLOC; 850 rc = TOXAV_ERR_CALL_MALLOC;
@@ -806,8 +857,6 @@ ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
806 void* tmp = realloc(av->calls, sizeof(ToxAVCall*) * friend_number + 1); 857 void* tmp = realloc(av->calls, sizeof(ToxAVCall*) * friend_number + 1);
807 858
808 if (tmp == NULL) { 859 if (tmp == NULL) {
809 pthread_mutex_destroy(call->mutex_control);
810 pthread_mutex_destroy(call->mutex_do);
811 free(call); 860 free(call);
812 call = NULL; 861 call = NULL;
813 rc = TOXAV_ERR_CALL_MALLOC; 862 rc = TOXAV_ERR_CALL_MALLOC;
@@ -843,6 +892,7 @@ END:
843 892
844ToxAVCall* call_get(ToxAV* av, uint32_t friend_number) 893ToxAVCall* call_get(ToxAV* av, uint32_t friend_number)
845{ 894{
895 /* Assumes mutex locked */
846 if (av->calls == NULL || av->calls_tail < friend_number) 896 if (av->calls == NULL || av->calls_tail < friend_number)
847 return NULL; 897 return NULL;
848 898
@@ -851,6 +901,8 @@ ToxAVCall* call_get(ToxAV* av, uint32_t friend_number)
851 901
852bool call_prepare_transmission(ToxAVCall* call) 902bool call_prepare_transmission(ToxAVCall* call)
853{ 903{
904 /* Assumes mutex locked */
905
854 if (call == NULL) 906 if (call == NULL)
855 return false; 907 return false;
856 908
@@ -860,25 +912,22 @@ bool call_prepare_transmission(ToxAVCall* call)
860 /* It makes no sense to have CSession without callbacks */ 912 /* It makes no sense to have CSession without callbacks */
861 return false; 913 return false;
862 914
863 pthread_mutex_lock(call->mutex_control);
864
865 if (call->active) { 915 if (call->active) {
866 pthread_mutex_unlock(call->mutex_control);
867 LOGGER_WARNING("Call already active!\n"); 916 LOGGER_WARNING("Call already active!\n");
868 return true; 917 return true;
869 } 918 }
870 919
871 if (pthread_mutex_init(call->mutex_encoding_audio, NULL) != 0) 920 if (pthread_mutex_init(call->mutex_audio_sending, NULL) != 0)
872 goto MUTEX_INIT_ERROR; 921 goto MUTEX_INIT_ERROR;
873 922
874 if (pthread_mutex_init(call->mutex_encoding_video, NULL) != 0) { 923 if (pthread_mutex_init(call->mutex_video_sending, NULL) != 0) {
875 pthread_mutex_destroy(call->mutex_encoding_audio); 924 pthread_mutex_destroy(call->mutex_audio_sending);
876 goto MUTEX_INIT_ERROR; 925 goto MUTEX_INIT_ERROR;
877 } 926 }
878 927
879 if (pthread_mutex_init(call->mutex_do, NULL) != 0) { 928 if (pthread_mutex_init(call->mutex_decoding, NULL) != 0) {
880 pthread_mutex_destroy(call->mutex_encoding_audio); 929 pthread_mutex_destroy(call->mutex_audio_sending);
881 pthread_mutex_destroy(call->mutex_encoding_video); 930 pthread_mutex_destroy(call->mutex_video_sending);
882 goto MUTEX_INIT_ERROR; 931 goto MUTEX_INIT_ERROR;
883 } 932 }
884 933
@@ -945,7 +994,6 @@ bool call_prepare_transmission(ToxAVCall* call)
945 } 994 }
946 995
947 call->active = 1; 996 call->active = 1;
948 pthread_mutex_unlock(call->mutex_control);
949 return true; 997 return true;
950 998
951FAILURE: 999FAILURE:
@@ -955,16 +1003,12 @@ FAILURE:
955 call->rtps[video_index] = NULL; 1003 call->rtps[video_index] = NULL;
956 cs_kill(call->cs); 1004 cs_kill(call->cs);
957 call->cs = NULL; 1005 call->cs = NULL;
958 call->active = 0; 1006 pthread_mutex_destroy(call->mutex_audio_sending);
959 pthread_mutex_destroy(call->mutex_encoding_audio); 1007 pthread_mutex_destroy(call->mutex_video_sending);
960 pthread_mutex_destroy(call->mutex_encoding_video); 1008 pthread_mutex_destroy(call->mutex_decoding);
961 pthread_mutex_destroy(call->mutex_do);
962
963 pthread_mutex_unlock(call->mutex_control);
964 return false; 1009 return false;
965 1010
966MUTEX_INIT_ERROR: 1011MUTEX_INIT_ERROR:
967 pthread_mutex_unlock(call->mutex_control);
968 LOGGER_ERROR("Mutex initialization failed!\n"); 1012 LOGGER_ERROR("Mutex initialization failed!\n");
969 return false; 1013 return false;
970} 1014}
@@ -974,29 +1018,26 @@ void call_kill_transmission(ToxAVCall* call)
974 if (call == NULL || call->active == 0) 1018 if (call == NULL || call->active == 0)
975 return; 1019 return;
976 1020
977 pthread_mutex_lock(call->mutex_control);
978
979 call->active = 0; 1021 call->active = 0;
980 1022
981 pthread_mutex_lock(call->mutex_encoding_audio); 1023 pthread_mutex_lock(call->mutex_audio_sending);
982 pthread_mutex_unlock(call->mutex_encoding_audio); 1024 pthread_mutex_unlock(call->mutex_audio_sending);
983 pthread_mutex_lock(call->mutex_encoding_video); 1025 pthread_mutex_lock(call->mutex_video_sending);
984 pthread_mutex_unlock(call->mutex_encoding_video); 1026 pthread_mutex_unlock(call->mutex_video_sending);
985 pthread_mutex_lock(call->mutex_do); 1027 pthread_mutex_lock(call->mutex_decoding);
986 pthread_mutex_unlock(call->mutex_do); 1028 pthread_mutex_unlock(call->mutex_decoding);
987 1029
988 rtp_kill(call->rtps[audio_index]); 1030 rtp_kill(call->rtps[audio_index]);
989 call->rtps[audio_index] = NULL; 1031 call->rtps[audio_index] = NULL;
990 rtp_kill(call->rtps[video_index]); 1032 rtp_kill(call->rtps[video_index]);
991 call->rtps[video_index] = NULL; 1033 call->rtps[video_index] = NULL;
1034
992 cs_kill(call->cs); 1035 cs_kill(call->cs);
993 call->cs = NULL; 1036 call->cs = NULL;
994 1037
995 pthread_mutex_destroy(call->mutex_encoding_audio); 1038 pthread_mutex_destroy(call->mutex_audio_sending);
996 pthread_mutex_destroy(call->mutex_encoding_video); 1039 pthread_mutex_destroy(call->mutex_video_sending);
997 pthread_mutex_destroy(call->mutex_do); 1040 pthread_mutex_destroy(call->mutex_decoding);
998
999 pthread_mutex_unlock(call->mutex_control);
1000} 1041}
1001 1042
1002void call_remove(ToxAVCall* call) 1043void call_remove(ToxAVCall* call)
@@ -1010,9 +1051,6 @@ void call_remove(ToxAVCall* call)
1010 ToxAVCall* prev = call->prev; 1051 ToxAVCall* prev = call->prev;
1011 ToxAVCall* next = call->next; 1052 ToxAVCall* next = call->next;
1012 1053
1013 pthread_mutex_destroy(call->mutex_control);
1014 pthread_mutex_destroy(call->mutex_do);
1015
1016 free(call); 1054 free(call);
1017 1055
1018 if (prev) 1056 if (prev)
diff --git a/toxav/toxav.h b/toxav/toxav.h
index ec232ddd..101047ed 100644
--- a/toxav/toxav.h
+++ b/toxav/toxav.h
@@ -187,43 +187,41 @@ 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 * Not sending anything. Either the friend requested that this client stops 190 * Not sending nor receiving anything, meaning, one of the sides requested pause.
191 * sending anything, or the client turned off both audio and video by setting 191 * The call will be resumed once the side that initiated pause resumes it.
192 * the respective bit rates to 0.
193 *
194 * If both sides are in this state, the call is effectively on hold, but not
195 * in the PAUSED state.
196 */ 192 */
197 TOXAV_CALL_STATE_NOT_SENDING, 193 TOXAV_CALL_STATE_PAUSED = 0,
198 /** 194 /**
199 * Sending audio only. Either the friend requested that this client stops 195 * The flag that marks that friend is sending audio.
200 * sending video, or the client turned off video by setting the video bit rate
201 * to 0.
202 */ 196 */
203 TOXAV_CALL_STATE_SENDING_A, 197 TOXAV_CALL_STATE_SENDING_A = 1,
204 /** 198 /**
205 * Sending video only. Either the friend requested that this client stops 199 * The flag that marks that friend is sending video.
206 * sending audio (muted), or the client turned off audio by setting the audio
207 * bit rate to 0.
208 */ 200 */
209 TOXAV_CALL_STATE_SENDING_V, 201 TOXAV_CALL_STATE_SENDING_V = 2,
210 /** 202 /**
211 * Sending both audio and video. 203 * The flag that marks that friend is receiving audio.
212 */ 204 */
213 TOXAV_CALL_STATE_SENDING_AV, 205 TOXAV_CALL_STATE_RECEIVING_A = 4,
214 /** 206 /**
215 * The call is on hold. Both sides stop sending and receiving. 207 * The flag that marks that friend is receiving video.
216 */ 208 */
217 TOXAV_CALL_STATE_PAUSED, 209 TOXAV_CALL_STATE_RECEIVING_V = 8,
210
211 /**
212 * The core will never set TOXAV_CALL_STATE_END or TOXAV_CALL_STATE_ERROR
213 * together with other states.
214 */
215
218 /** 216 /**
219 * The call has finished. This is the final state after which no more state 217 * The call has finished. This is the final state after which no more state
220 * transitions can occur for the call. 218 * transitions can occur for the call.
221 */ 219 */
222 TOXAV_CALL_STATE_END, 220 TOXAV_CALL_STATE_END = 16,
223 /** 221 /**
224 * Sent by the AV core if an error occurred on the remote end. 222 * Sent by the AV core if an error occurred on the remote end.
225 */ 223 */
226 TOXAV_CALL_STATE_ERROR 224 TOXAV_CALL_STATE_ERROR = 32
227} TOXAV_CALL_STATE; 225} TOXAV_CALL_STATE;
228/** 226/**
229 * The function type for the `call_state` callback. 227 * The function type for the `call_state` callback.
@@ -231,7 +229,7 @@ typedef enum TOXAV_CALL_STATE {
231 * @param friend_number The friend number for which the call state changed. 229 * @param friend_number The friend number for which the call state changed.
232 * @param state The new call state. 230 * @param state The new call state.
233 */ 231 */
234typedef void toxav_call_state_cb(ToxAV *av, uint32_t friend_number, TOXAV_CALL_STATE state, void *user_data); 232typedef void toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data);
235/** 233/**
236 * Set the callback for the `call_state` event. Pass NULL to unset. 234 * Set the callback for the `call_state` event. Pass NULL to unset.
237 * 235 *
@@ -261,27 +259,19 @@ typedef enum TOXAV_CALL_CONTROL {
261 /** 259 /**
262 * Request that the friend stops sending audio. Regardless of the friend's 260 * Request that the friend stops sending audio. Regardless of the friend's
263 * compliance, this will cause the `receive_audio_frame` event to stop being 261 * compliance, this will cause the `receive_audio_frame` event to stop being
264 * triggered on receiving an audio frame from the friend. 262 * triggered on receiving an audio frame from the friend. If the audio was
263 * already muted, calling this control will notify client to start sending
264 * audio again.
265 */ 265 */
266 TOXAV_CALL_CONTROL_MUTE_AUDIO, 266 TOXAV_CALL_CONTROL_TOGGLE_MUTE_AUDIO,
267 /** 267 /**
268 * Request that the friend stops sending video. Regardless of the friend's 268 * Request that the friend stops sending video. Regardless of the friend's
269 * compliance, this will cause the `receive_video_frame` event to stop being 269 * compliance, this will cause the `receive_video_frame` event to stop being
270 * triggered on receiving an video frame from the friend. 270 * triggered on receiving an video frame from the friend. If the video was
271 */ 271 * already muted, calling this control will notify client to start sending
272 TOXAV_CALL_CONTROL_MUTE_VIDEO, 272 * video again.
273 /**
274 * Notify the friend that we are AGAIN ready to handle incoming audio.
275 * This control will not work if the call is not started with audio
276 * initiated.
277 */
278 TOXAV_CALL_CONTROL_UNMUTE_AUDIO,
279 /**
280 * Notify the friend that we are AGAIN ready to handle incoming video.
281 * This control will not work if the call is not started with video
282 * initiated.
283 */ 273 */
284 TOXAV_CALL_CONTROL_UNMUTE_VIDEO 274 TOXAV_CALL_CONTROL_TOGGLE_MUTE_VIDEO,
285} TOXAV_CALL_CONTROL; 275} TOXAV_CALL_CONTROL;
286typedef enum TOXAV_ERR_CALL_CONTROL { 276typedef enum TOXAV_ERR_CALL_CONTROL {
287 TOXAV_ERR_CALL_CONTROL_OK, 277 TOXAV_ERR_CALL_CONTROL_OK,