diff options
-rw-r--r-- | toxav/codec.c | 529 | ||||
-rw-r--r-- | toxav/codec.h | 67 | ||||
-rw-r--r-- | toxav/msi.c | 14 | ||||
-rw-r--r-- | toxav/msi.h | 4 | ||||
-rw-r--r-- | toxav/rtp.c | 100 | ||||
-rw-r--r-- | toxav/rtp.h | 8 | ||||
-rw-r--r-- | toxav/toxav.c | 8 | ||||
-rw-r--r-- | toxav/toxav_new.c | 710 | ||||
-rw-r--r-- | toxav/toxav_new.h | 481 | ||||
-rw-r--r-- | toxcore/logger.c | 4 | ||||
-rw-r--r-- | toxcore/util.h | 1 |
11 files changed, 1642 insertions, 284 deletions
diff --git a/toxav/codec.c b/toxav/codec.c index dae35d54..fd2f9f93 100644 --- a/toxav/codec.c +++ b/toxav/codec.c | |||
@@ -31,6 +31,7 @@ | |||
31 | 31 | ||
32 | #include <stdio.h> | 32 | #include <stdio.h> |
33 | #include <stdlib.h> | 33 | #include <stdlib.h> |
34 | #include <stdbool.h> | ||
34 | #include <math.h> | 35 | #include <math.h> |
35 | #include <assert.h> | 36 | #include <assert.h> |
36 | #include <time.h> | 37 | #include <time.h> |
@@ -39,6 +40,9 @@ | |||
39 | #include "rtp.h" | 40 | #include "rtp.h" |
40 | #include "codec.h" | 41 | #include "codec.h" |
41 | 42 | ||
43 | |||
44 | #define DEFAULT_JBUF 6 | ||
45 | |||
42 | /* Good quality encode. */ | 46 | /* Good quality encode. */ |
43 | #define MAX_ENCODE_TIME_US VPX_DL_GOOD_QUALITY | 47 | #define MAX_ENCODE_TIME_US VPX_DL_GOOD_QUALITY |
44 | #define MAX_DECODE_TIME_US 0 | 48 | #define MAX_DECODE_TIME_US 0 |
@@ -62,12 +66,12 @@ typedef struct { | |||
62 | Payload **packets; | 66 | Payload **packets; |
63 | } PayloadBuffer; | 67 | } PayloadBuffer; |
64 | 68 | ||
65 | static _Bool buffer_full(const PayloadBuffer *b) | 69 | static bool buffer_full(const PayloadBuffer *b) |
66 | { | 70 | { |
67 | return (b->end + 1) % b->size == b->start; | 71 | return (b->end + 1) % b->size == b->start; |
68 | } | 72 | } |
69 | 73 | ||
70 | static _Bool buffer_empty(const PayloadBuffer *b) | 74 | static bool buffer_empty(const PayloadBuffer *b) |
71 | { | 75 | { |
72 | return b->end == b->start; | 76 | return b->end == b->start; |
73 | } | 77 | } |
@@ -121,7 +125,7 @@ static void buffer_free(PayloadBuffer *b) | |||
121 | } | 125 | } |
122 | 126 | ||
123 | /* JITTER BUFFER WORK */ | 127 | /* JITTER BUFFER WORK */ |
124 | typedef struct _JitterBuffer { | 128 | typedef struct { |
125 | RTPMessage **queue; | 129 | RTPMessage **queue; |
126 | uint32_t size; | 130 | uint32_t size; |
127 | uint32_t capacity; | 131 | uint32_t capacity; |
@@ -225,99 +229,9 @@ static RTPMessage *jbuf_read(JitterBuffer *q, int32_t *success) | |||
225 | return NULL; | 229 | return NULL; |
226 | } | 230 | } |
227 | 231 | ||
228 | static int init_video_decoder(CSSession *cs) | ||
229 | { | ||
230 | int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION); | ||
231 | |||
232 | if ( rc != VPX_CODEC_OK) { | ||
233 | LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); | ||
234 | return -1; | ||
235 | } | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | static int init_audio_decoder(CSSession *cs) | ||
241 | { | ||
242 | int rc; | ||
243 | cs->audio_decoder = opus_decoder_create(cs->audio_decoder_sample_rate, cs->audio_decoder_channels, &rc ); | ||
244 | |||
245 | if ( rc != OPUS_OK ) { | ||
246 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); | ||
247 | return -1; | ||
248 | } | ||
249 | |||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int init_video_encoder(CSSession *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate) | ||
254 | { | ||
255 | vpx_codec_enc_cfg_t cfg; | ||
256 | int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); | ||
257 | |||
258 | if (rc != VPX_CODEC_OK) { | ||
259 | LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); | ||
260 | return -1; | ||
261 | } | ||
262 | |||
263 | cfg.rc_target_bitrate = video_bitrate; | ||
264 | cfg.g_w = max_width; | ||
265 | cfg.g_h = max_height; | ||
266 | cfg.g_pass = VPX_RC_ONE_PASS; | ||
267 | cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS; | ||
268 | cfg.g_lag_in_frames = 0; | ||
269 | cfg.kf_min_dist = 0; | ||
270 | cfg.kf_max_dist = 48; | ||
271 | cfg.kf_mode = VPX_KF_AUTO; | ||
272 | |||
273 | rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION); | ||
274 | |||
275 | if ( rc != VPX_CODEC_OK) { | ||
276 | LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); | ||
277 | return -1; | ||
278 | } | ||
279 | |||
280 | rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 8); | ||
281 | |||
282 | if ( rc != VPX_CODEC_OK) { | ||
283 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
284 | return -1; | ||
285 | } | ||
286 | |||
287 | cs->max_width = max_width; | ||
288 | cs->max_height = max_height; | ||
289 | cs->video_bitrate = video_bitrate; | ||
290 | |||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | static int init_audio_encoder(CSSession *cs) | ||
295 | { | ||
296 | int rc = OPUS_OK; | ||
297 | cs->audio_encoder = opus_encoder_create(cs->audio_encoder_sample_rate, | ||
298 | cs->audio_encoder_channels, OPUS_APPLICATION_AUDIO, &rc); | ||
299 | |||
300 | if ( rc != OPUS_OK ) { | ||
301 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | ||
302 | return -1; | ||
303 | } | ||
304 | |||
305 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_encoder_bitrate)); | ||
306 | 232 | ||
307 | if ( rc != OPUS_OK ) { | ||
308 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
309 | return -1; | ||
310 | } | ||
311 | 233 | ||
312 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); | ||
313 | 234 | ||
314 | if ( rc != OPUS_OK ) { | ||
315 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
316 | return -1; | ||
317 | } | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | 235 | ||
322 | /* PUBLIC */ | 236 | /* PUBLIC */ |
323 | int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length) | 237 | int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length) |
@@ -406,17 +320,17 @@ void cs_do(CSSession *cs) | |||
406 | /* Leave space for (possibly) other thread to queue more data after we read it here */ | 320 | /* Leave space for (possibly) other thread to queue more data after we read it here */ |
407 | pthread_mutex_unlock(cs->queue_mutex); | 321 | pthread_mutex_unlock(cs->queue_mutex); |
408 | 322 | ||
409 | rc = vpx_codec_decode(&cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); | 323 | rc = vpx_codec_decode(cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US); |
410 | free(p); | 324 | free(p); |
411 | 325 | ||
412 | if (rc != VPX_CODEC_OK) { | 326 | if (rc != VPX_CODEC_OK) { |
413 | LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); | 327 | LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc)); |
414 | } else { | 328 | } else { |
415 | vpx_codec_iter_t iter = NULL; | 329 | vpx_codec_iter_t iter = NULL; |
416 | vpx_image_t *dest = vpx_codec_get_frame(&cs->v_decoder, &iter); | 330 | vpx_image_t *dest = vpx_codec_get_frame(cs->v_decoder, &iter); |
417 | 331 | ||
418 | /* Play decoded images */ | 332 | /* Play decoded images */ |
419 | for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) { | 333 | for (; dest; dest = vpx_codec_get_frame(cs->v_decoder, &iter)) { |
420 | if (cs->vcb.first) | 334 | if (cs->vcb.first) |
421 | cs->vcb.first(cs->agent, cs->call_idx, dest, cs->vcb.second); | 335 | cs->vcb.first(cs->agent, cs->call_idx, dest, cs->vcb.second); |
422 | 336 | ||
@@ -430,13 +344,17 @@ void cs_do(CSSession *cs) | |||
430 | pthread_mutex_unlock(cs->queue_mutex); | 344 | pthread_mutex_unlock(cs->queue_mutex); |
431 | } | 345 | } |
432 | 346 | ||
433 | int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height) | 347 | int cs_set_sending_video_resolution(CSSession *cs, uint16_t width, uint16_t height) |
434 | { | 348 | { |
435 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | 349 | if (!cs->v_encoding) |
436 | 350 | return -1; | |
351 | |||
352 | /* TODO FIXME reference is safe? */ | ||
353 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; | ||
354 | |||
437 | if (cfg.g_w == width && cfg.g_h == height) | 355 | if (cfg.g_w == width && cfg.g_h == height) |
438 | return 0; | 356 | return 0; |
439 | 357 | /* | |
440 | if (width * height > cs->max_width * cs->max_height) { | 358 | if (width * height > cs->max_width * cs->max_height) { |
441 | vpx_codec_ctx_t v_encoder = cs->v_encoder; | 359 | vpx_codec_ctx_t v_encoder = cs->v_encoder; |
442 | 360 | ||
@@ -447,12 +365,12 @@ int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t heig | |||
447 | 365 | ||
448 | vpx_codec_destroy(&v_encoder); | 366 | vpx_codec_destroy(&v_encoder); |
449 | return 0; | 367 | return 0; |
450 | } | 368 | }*/ |
451 | 369 | ||
452 | LOGGER_DEBUG("New video resolution: %u %u", width, height); | 370 | LOGGER_DEBUG("New video resolution: %u %u", width, height); |
453 | cfg.g_w = width; | 371 | cfg.g_w = width; |
454 | cfg.g_h = height; | 372 | cfg.g_h = height; |
455 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | 373 | int rc = vpx_codec_enc_config_set(cs->v_encoder, &cfg); |
456 | 374 | ||
457 | if ( rc != VPX_CODEC_OK) { | 375 | if ( rc != VPX_CODEC_OK) { |
458 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | 376 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); |
@@ -462,27 +380,107 @@ int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t heig | |||
462 | return 0; | 380 | return 0; |
463 | } | 381 | } |
464 | 382 | ||
465 | int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate) | 383 | int cs_set_sending_video_bitrate(CSSession *cs, uint32_t bitrate) |
466 | { | 384 | { |
467 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc; | 385 | if (!cs->v_encoding) |
468 | 386 | return -1; | |
469 | if (cfg.rc_target_bitrate == video_bitrate) | 387 | |
388 | /* TODO FIXME reference is safe? */ | ||
389 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; | ||
390 | if (cfg.rc_target_bitrate == bitrate) | ||
470 | return 0; | 391 | return 0; |
471 | 392 | ||
472 | LOGGER_DEBUG("New video bitrate: %u", video_bitrate); | 393 | LOGGER_DEBUG("New video bitrate: %u", video_bitrate); |
473 | cfg.rc_target_bitrate = video_bitrate; | 394 | cfg.rc_target_bitrate = bitrate; |
474 | int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg); | 395 | |
475 | 396 | int rc = vpx_codec_enc_config_set(cs->v_encoder, &cfg); | |
476 | if ( rc != VPX_CODEC_OK) { | 397 | if ( rc != VPX_CODEC_OK) { |
477 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | 398 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); |
478 | return cs_ErrorSettingVideoBitrate; | 399 | return cs_ErrorSettingVideoBitrate; |
479 | } | 400 | } |
480 | 401 | ||
481 | cs->video_bitrate = video_bitrate; | ||
482 | return 0; | 402 | return 0; |
483 | } | 403 | } |
484 | 404 | ||
485 | CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video) | 405 | int cs_set_sending_audio_bitrate(CSSession *cs, int32_t rate) |
406 | { | ||
407 | if (cs->audio_encoder == NULL) | ||
408 | return -1; | ||
409 | |||
410 | int rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(rate)); | ||
411 | |||
412 | if ( rc != OPUS_OK ) { | ||
413 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
414 | return -1; | ||
415 | } | ||
416 | |||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | int cs_set_sending_audio_sampling_rate(CSSession* cs, int32_t rate) | ||
421 | { | ||
422 | /* TODO Find a better way? */ | ||
423 | if (cs->audio_encoder == NULL) | ||
424 | return -1; | ||
425 | |||
426 | int rc = OPUS_OK; | ||
427 | int last_rate = 0; | ||
428 | |||
429 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(&last_rate)); | ||
430 | |||
431 | if ( rc != OPUS_OK ) { | ||
432 | LOGGER_ERROR("Error while getting encoder ctl: %s", opus_strerror(rc)); | ||
433 | return -1; | ||
434 | } | ||
435 | |||
436 | if (rate == last_rate) | ||
437 | return 0; | ||
438 | |||
439 | OpusEncoder* new_enc = opus_encoder_create(rate, cs->channels, OPUS_APPLICATION_AUDIO, &rc); | ||
440 | |||
441 | if ( rc != OPUS_OK ) { | ||
442 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | ||
443 | return -1; | ||
444 | } | ||
445 | |||
446 | opus_encoder_destroy(cs->audio_encoder); | ||
447 | cs->audio_encoder = new_enc; | ||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | int cs_set_sending_audio_channels(CSSession* cs, int32_t count) | ||
452 | { | ||
453 | /* TODO Find a better way? */ | ||
454 | if (cs->audio_encoder == NULL) | ||
455 | return -1; | ||
456 | |||
457 | if (cs->channels == count) | ||
458 | return 0; | ||
459 | |||
460 | int rc = OPUS_OK; | ||
461 | int bitrate = 0; | ||
462 | |||
463 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(&bitrate)); | ||
464 | |||
465 | if ( rc != OPUS_OK ) { | ||
466 | LOGGER_ERROR("Error while getting encoder ctl: %s", opus_strerror(rc)); | ||
467 | return -1; | ||
468 | } | ||
469 | |||
470 | cs->channels = count; | ||
471 | OpusEncoder* new_enc = opus_encoder_create(bitrate, cs->channels, OPUS_APPLICATION_AUDIO, &rc); | ||
472 | |||
473 | if ( rc != OPUS_OK ) { | ||
474 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | ||
475 | return -1; | ||
476 | } | ||
477 | |||
478 | opus_encoder_destroy(cs->audio_encoder); | ||
479 | cs->audio_encoder = new_enc; | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | CSSession *cs_new(uint32_t s_audio_b, uint32_t p_audio_b, uint32_t s_video_b, uint32_t p_video_b) | ||
486 | { | 484 | { |
487 | CSSession *cs = calloc(sizeof(CSSession), 1); | 485 | CSSession *cs = calloc(sizeof(CSSession), 1); |
488 | 486 | ||
@@ -491,106 +489,265 @@ CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, | |||
491 | return NULL; | 489 | return NULL; |
492 | } | 490 | } |
493 | 491 | ||
494 | if (create_recursive_mutex(cs->queue_mutex) != 0) { | 492 | /* TODO this has to be exchanged in msi */ |
495 | LOGGER_WARNING("Failed to create recursive mutex!"); | 493 | cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE; |
496 | free(cs); | 494 | cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE; |
497 | return NULL; | 495 | |
496 | if (s_audio_b > 0 && 0 != cs_enable_audio_sending(cs, s_audio_b)) { /* Sending audio enabled */ | ||
497 | LOGGER_WARNING("Failed to enable audio sending!"); | ||
498 | goto FAILURE; | ||
498 | } | 499 | } |
499 | 500 | ||
500 | if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) { | 501 | if (p_audio_b > 0 && 0 != cs_enable_audio_receiving(cs)) { /* Receiving audio enabled */ |
501 | LOGGER_WARNING("Jitter buffer creaton failed!"); | 502 | LOGGER_WARNING("Failed to enable audio receiving!"); |
502 | goto error; | 503 | goto FAILURE; |
503 | } | 504 | } |
504 | 505 | ||
505 | cs->audio_encoder_bitrate = cs_self->audio_bitrate; | 506 | if (s_video_b > 0 && 0 != cs_enable_video_sending(cs, s_video_b)) { /* Sending video enabled */ |
506 | cs->audio_encoder_sample_rate = cs_self->audio_sample_rate; | 507 | LOGGER_WARNING("Failed to enable video sending!"); |
507 | cs->audio_encoder_channels = cs_self->audio_channels; | 508 | goto FAILURE; |
508 | cs->audio_encoder_frame_duration = cs_self->audio_frame_duration; | 509 | } |
509 | 510 | ||
510 | cs->audio_decoder_bitrate = cs_peer->audio_bitrate; | 511 | if (p_video_b > 0 && 0 != cs_enable_video_receiving(cs)) { /* Receiving video enabled */ |
511 | cs->audio_decoder_sample_rate = cs_peer->audio_sample_rate; | 512 | LOGGER_WARNING("Failed to enable video receiving!"); |
512 | cs->audio_decoder_channels = cs_peer->audio_channels; | 513 | goto FAILURE; |
513 | cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration; | ||
514 | |||
515 | |||
516 | cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? cs_AudioEncoding : 0; | ||
517 | cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? cs_AudioDecoding : 0; | ||
518 | |||
519 | if ( !(cs->capabilities & cs_AudioEncoding) || !(cs->capabilities & cs_AudioDecoding) ) goto error; | ||
520 | |||
521 | if ((cs->support_video = has_video)) { | ||
522 | cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE; | ||
523 | cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE; | ||
524 | |||
525 | cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width, | ||
526 | cs_self->max_video_height, cs_self->video_bitrate) ) ? cs_VideoEncoding : 0; | ||
527 | cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? cs_VideoDecoding : 0; | ||
528 | |||
529 | if ( !(cs->capabilities & cs_VideoEncoding) || !(cs->capabilities & cs_VideoDecoding) ) goto error; | ||
530 | |||
531 | if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) goto error; | ||
532 | |||
533 | if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) ) | ||
534 | goto error; | ||
535 | |||
536 | if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error; | ||
537 | } | 514 | } |
538 | 515 | ||
539 | return cs; | 516 | return cs; |
540 | 517 | ||
541 | error: | 518 | FAILURE: |
542 | LOGGER_WARNING("Error initializing codec session! Application might misbehave!"); | 519 | LOGGER_WARNING("Error initializing codec session! Application might misbehave!"); |
543 | 520 | ||
544 | pthread_mutex_destroy(cs->queue_mutex); | 521 | cs_disable_audio_sending(cs); |
545 | 522 | cs_disable_audio_receiving(cs); | |
546 | if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder); | 523 | cs_disable_video_sending(cs); |
547 | 524 | cs_disable_video_receiving(cs); | |
548 | if ( cs->audio_decoder ) opus_decoder_destroy(cs->audio_decoder); | 525 | |
549 | 526 | free(cs); | |
550 | |||
551 | if (has_video) { | ||
552 | if ( cs->capabilities & cs_VideoDecoding ) vpx_codec_destroy(&cs->v_decoder); | ||
553 | 527 | ||
554 | if ( cs->capabilities & cs_VideoEncoding ) vpx_codec_destroy(&cs->v_encoder); | 528 | return NULL; |
529 | } | ||
555 | 530 | ||
556 | buffer_free(cs->vbuf_raw); | 531 | void cs_kill(CSSession *cs) |
532 | { | ||
533 | if (!cs) | ||
534 | return; | ||
557 | 535 | ||
558 | free(cs->frame_buf); | 536 | /* NOTE: queue_message() will not be called since |
559 | free(cs->split_video_frame); | 537 | * the callback is unregistered before cs_kill is called. |
560 | } | 538 | */ |
539 | |||
540 | cs_disable_audio_sending(cs); | ||
541 | cs_disable_audio_receiving(cs); | ||
542 | cs_disable_video_sending(cs); | ||
543 | cs_disable_video_receiving(cs); | ||
561 | 544 | ||
562 | jbuf_free(cs->j_buf); | 545 | LOGGER_DEBUG("Terminated codec state: %p", cs); |
563 | free(cs); | 546 | free(cs); |
547 | } | ||
564 | 548 | ||
565 | return NULL; | 549 | int cs_enable_audio_sending(CSSession* cs, uint32_t bitrate) |
550 | { | ||
551 | if (cs->audio_encoder) | ||
552 | return 0; | ||
553 | |||
554 | /** | ||
555 | * Encoder is initialized with default values. These values (Sampling rate, channel count) | ||
556 | * change on the fly from toxav. | ||
557 | */ | ||
558 | |||
559 | int rc = OPUS_OK; | ||
560 | cs->audio_encoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_AUDIO, &rc); | ||
561 | |||
562 | if ( rc != OPUS_OK ) { | ||
563 | LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); | ||
564 | return -1; | ||
565 | } | ||
566 | |||
567 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(bitrate)); | ||
568 | |||
569 | if ( rc != OPUS_OK ) { | ||
570 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
571 | goto FAILURE; | ||
572 | } | ||
573 | |||
574 | rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); | ||
575 | |||
576 | if ( rc != OPUS_OK ) { | ||
577 | LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); | ||
578 | goto FAILURE; | ||
579 | } | ||
580 | |||
581 | cs->channels = 2; | ||
582 | return 0; | ||
583 | |||
584 | FAILURE: | ||
585 | cs_disable_audio_sending(cs); | ||
586 | return -1; | ||
566 | } | 587 | } |
567 | 588 | ||
568 | void cs_kill(CSSession *cs) | 589 | int cs_enable_audio_receiving(CSSession* cs) |
569 | { | 590 | { |
570 | if (!cs) return; | 591 | if (cs->audio_decoder) |
592 | return 0; | ||
593 | |||
594 | /** | ||
595 | * Decoder is initialized with default values. These values (Sampling rate, channel count) | ||
596 | * change on the fly from toxav. | ||
597 | */ | ||
598 | |||
599 | int rc; | ||
600 | cs->audio_decoder = opus_decoder_create(48000, 2, &rc ); | ||
601 | |||
602 | if ( rc != OPUS_OK ) { | ||
603 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); | ||
604 | return -1; | ||
605 | } | ||
606 | |||
607 | |||
608 | if ( !(cs->j_buf = jbuf_new(DEFAULT_JBUF)) ) { | ||
609 | LOGGER_WARNING("Jitter buffer creaton failed!"); | ||
610 | opus_decoder_destroy(cs->audio_decoder); | ||
611 | cs->audio_decoder = NULL; | ||
612 | return -1; | ||
613 | } | ||
614 | |||
615 | |||
616 | return 0; | ||
617 | } | ||
571 | 618 | ||
572 | /* queue_message will not be called since it's unregistered before cs_kill is called */ | 619 | int cs_enable_video_sending(CSSession* cs, uint32_t bitrate) |
573 | pthread_mutex_destroy(cs->queue_mutex); | 620 | { |
621 | if (cs->v_encoding) | ||
622 | return 0; | ||
623 | |||
624 | vpx_codec_enc_cfg_t cfg; | ||
625 | int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0); | ||
626 | |||
627 | if (rc != VPX_CODEC_OK) { | ||
628 | LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc)); | ||
629 | return -1; | ||
630 | } | ||
631 | |||
632 | rc = vpx_codec_enc_init_ver(cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, | ||
633 | VPX_ENCODER_ABI_VERSION); | ||
634 | |||
635 | if ( rc != VPX_CODEC_OK) { | ||
636 | LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc)); | ||
637 | return -1; | ||
638 | } | ||
639 | |||
640 | /* So that we can use cs_disable_video_sending to clean up */ | ||
641 | cs->v_encoding = true; | ||
642 | |||
643 | if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) ) | ||
644 | goto FAILURE; | ||
645 | |||
646 | cfg.rc_target_bitrate = bitrate; | ||
647 | cfg.g_w = 800; | ||
648 | cfg.g_h = 600; | ||
649 | cfg.g_pass = VPX_RC_ONE_PASS; | ||
650 | cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS; | ||
651 | cfg.g_lag_in_frames = 0; | ||
652 | cfg.kf_min_dist = 0; | ||
653 | cfg.kf_max_dist = 48; | ||
654 | cfg.kf_mode = VPX_KF_AUTO; | ||
655 | |||
656 | |||
657 | rc = vpx_codec_control(cs->v_encoder, VP8E_SET_CPUUSED, 8); | ||
658 | |||
659 | if ( rc != VPX_CODEC_OK) { | ||
660 | LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc)); | ||
661 | goto FAILURE; | ||
662 | } | ||
663 | |||
664 | return 0; | ||
665 | |||
666 | FAILURE: | ||
667 | cs_disable_video_sending(cs); | ||
668 | return -1; | ||
669 | } | ||
574 | 670 | ||
671 | int cs_enable_video_receiving(CSSession* cs) | ||
672 | { | ||
673 | if (cs->v_decoding) | ||
674 | return 0; | ||
675 | |||
676 | if (create_recursive_mutex(cs->queue_mutex) != 0) { | ||
677 | LOGGER_WARNING("Failed to create recursive mutex!"); | ||
678 | return -1; | ||
679 | } | ||
680 | |||
681 | int rc = vpx_codec_dec_init_ver(cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, | ||
682 | NULL, 0, VPX_DECODER_ABI_VERSION); | ||
683 | |||
684 | if ( rc != VPX_CODEC_OK) { | ||
685 | LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc)); | ||
686 | |||
687 | pthread_mutex_destroy(cs->queue_mutex); | ||
688 | return -1; | ||
689 | } | ||
690 | |||
691 | /* So that we can use cs_disable_video_sending to clean up */ | ||
692 | cs->v_decoding = true; | ||
693 | |||
694 | if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) | ||
695 | goto FAILURE; | ||
696 | |||
697 | if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) | ||
698 | goto FAILURE; | ||
699 | |||
700 | return 0; | ||
701 | |||
702 | FAILURE: | ||
703 | cs_disable_video_receiving(cs); | ||
704 | return -1; | ||
705 | } | ||
575 | 706 | ||
576 | if ( cs->audio_encoder ) | 707 | void cs_disable_audio_sending(CSSession* cs) |
708 | { | ||
709 | if ( cs->audio_encoder ) { | ||
577 | opus_encoder_destroy(cs->audio_encoder); | 710 | opus_encoder_destroy(cs->audio_encoder); |
711 | cs->audio_encoder = NULL; | ||
712 | cs->channels = 0; | ||
713 | } | ||
714 | } | ||
578 | 715 | ||
579 | if ( cs->audio_decoder ) | 716 | void cs_disable_audio_receiving(CSSession* cs) |
717 | { | ||
718 | if ( cs->audio_decoder ) { | ||
580 | opus_decoder_destroy(cs->audio_decoder); | 719 | opus_decoder_destroy(cs->audio_decoder); |
720 | cs->audio_decoder = NULL; | ||
721 | jbuf_free(cs->j_buf); | ||
722 | cs->j_buf = NULL; | ||
723 | } | ||
724 | } | ||
581 | 725 | ||
582 | if ( cs->capabilities & cs_VideoDecoding ) | 726 | void cs_disable_video_sending(CSSession* cs) |
583 | vpx_codec_destroy(&cs->v_decoder); | 727 | { |
584 | 728 | if (cs->v_encoding) { | |
585 | if ( cs->capabilities & cs_VideoEncoding ) | 729 | cs->v_encoding = false; |
586 | vpx_codec_destroy(&cs->v_encoder); | 730 | |
587 | 731 | free(cs->split_video_frame); | |
588 | jbuf_free(cs->j_buf); | 732 | cs->split_video_frame = NULL; |
589 | buffer_free(cs->vbuf_raw); | 733 | |
590 | free(cs->frame_buf); | 734 | vpx_codec_destroy(cs->v_encoder); |
735 | } | ||
736 | } | ||
591 | 737 | ||
592 | LOGGER_DEBUG("Terminated codec state: %p", cs); | 738 | void cs_disable_video_receiving(CSSession* cs) |
593 | free(cs); | 739 | { |
740 | if (cs->v_decoding) { | ||
741 | cs->v_decoding = false; | ||
742 | |||
743 | buffer_free(cs->vbuf_raw); | ||
744 | cs->vbuf_raw = NULL; | ||
745 | free(cs->frame_buf); | ||
746 | cs->frame_buf = NULL; | ||
747 | |||
748 | vpx_codec_destroy(cs->v_decoder); | ||
749 | pthread_mutex_destroy(cs->queue_mutex); | ||
750 | } | ||
594 | } | 751 | } |
595 | 752 | ||
596 | 753 | ||
diff --git a/toxav/codec.h b/toxav/codec.h index 6018e5df..92262ef8 100644 --- a/toxav/codec.h +++ b/toxav/codec.h | |||
@@ -75,18 +75,16 @@ typedef struct _CSSession { | |||
75 | * | 75 | * |
76 | * | 76 | * |
77 | */ | 77 | */ |
78 | int support_video; | ||
79 | 78 | ||
80 | /* video encoding */ | 79 | /* video encoding */ |
81 | vpx_codec_ctx_t v_encoder; | 80 | vpx_codec_ctx_t v_encoder[1]; |
81 | bool v_encoding; | ||
82 | uint32_t frame_counter; | 82 | uint32_t frame_counter; |
83 | 83 | ||
84 | /* video decoding */ | 84 | /* video decoding */ |
85 | vpx_codec_ctx_t v_decoder; | 85 | vpx_codec_ctx_t v_decoder[1]; |
86 | int max_width; | 86 | bool v_decoding; |
87 | int max_height; | 87 | void *vbuf_raw; /* Un-decoded data */ |
88 | unsigned int video_bitrate; | ||
89 | |||
90 | 88 | ||
91 | /* Data handling */ | 89 | /* Data handling */ |
92 | uint8_t *frame_buf; /* buffer for split video payloads */ | 90 | uint8_t *frame_buf; /* buffer for split video payloads */ |
@@ -112,18 +110,10 @@ typedef struct _CSSession { | |||
112 | 110 | ||
113 | /* audio encoding */ | 111 | /* audio encoding */ |
114 | OpusEncoder *audio_encoder; | 112 | OpusEncoder *audio_encoder; |
115 | int audio_encoder_bitrate; | 113 | int32_t channels; |
116 | int audio_encoder_sample_rate; | 114 | |
117 | int audio_encoder_frame_duration; | ||
118 | int audio_encoder_channels; | ||
119 | |||
120 | /* audio decoding */ | 115 | /* audio decoding */ |
121 | OpusDecoder *audio_decoder; | 116 | OpusDecoder *audio_decoder; |
122 | int audio_decoder_bitrate; | ||
123 | int audio_decoder_sample_rate; | ||
124 | int audio_decoder_frame_duration; | ||
125 | int audio_decoder_channels; | ||
126 | |||
127 | struct _JitterBuffer *j_buf; | 117 | struct _JitterBuffer *j_buf; |
128 | 118 | ||
129 | 119 | ||
@@ -138,25 +128,16 @@ typedef struct _CSSession { | |||
138 | * | 128 | * |
139 | */ | 129 | */ |
140 | 130 | ||
141 | uint64_t capabilities; /* supports*/ | ||
142 | |||
143 | /* Callbacks */ | 131 | /* Callbacks */ |
144 | PAIR(CSAudioCallback, void *) acb; | 132 | PAIR(CSAudioCallback, void *) acb; |
145 | PAIR(CSVideoCallback, void *) vcb; | 133 | PAIR(CSVideoCallback, void *) vcb; |
146 | 134 | ||
147 | /* Buffering */ | ||
148 | void *vbuf_raw; /* Un-decoded data */ | ||
149 | pthread_mutex_t queue_mutex[1]; | ||
150 | |||
151 | void *agent; /* Pointer to ToxAv */ | 135 | void *agent; /* Pointer to ToxAv */ |
152 | int32_t call_idx; | 136 | int32_t call_idx; |
137 | |||
138 | pthread_mutex_t queue_mutex[1]; | ||
153 | } CSSession; | 139 | } CSSession; |
154 | 140 | ||
155 | /* Make sure to be called BEFORE corresponding rtp_new */ | ||
156 | CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video); | ||
157 | /* Make sure to be called AFTER corresponding rtp_kill */ | ||
158 | void cs_kill(CSSession *cs); | ||
159 | |||
160 | int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length); | 141 | int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length); |
161 | const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size); | 142 | const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size); |
162 | 143 | ||
@@ -165,11 +146,35 @@ const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size); | |||
165 | */ | 146 | */ |
166 | void cs_do(CSSession *cs); | 147 | void cs_do(CSSession *cs); |
167 | 148 | ||
149 | /** | ||
150 | * Reconfigure video settings; return 0 on success or -1 on failure. | ||
151 | */ | ||
152 | int cs_set_sending_video_resolution(CSSession *cs, uint16_t width, uint16_t height); | ||
153 | int cs_set_sending_video_bitrate(CSSession *cs, uint32_t bitrate); | ||
154 | |||
155 | int cs_set_sending_audio_bitrate(CSSession* cs, int32_t rate); | ||
156 | /* NOTE: Try not to call these a lot */ | ||
157 | int cs_set_sending_audio_sampling_rate(CSSession* cs, int32_t rate); | ||
158 | int cs_set_sending_audio_channels(CSSession* cs, int32_t count); | ||
159 | |||
160 | /** | ||
161 | * Make sure to be called BEFORE corresponding rtp_new | ||
162 | */ | ||
163 | CSSession *cs_new(uint32_t s_audio_b, uint32_t p_audio_b, uint32_t s_video_b, uint32_t p_video_b); | ||
164 | /** | ||
165 | * Make sure to be called AFTER corresponding rtp_kill | ||
166 | */ | ||
167 | void cs_kill(CSSession *cs); | ||
168 | 168 | ||
169 | /* Reconfigure video encoder; return 0 on success or -1 on failure. */ | 169 | int cs_enable_audio_sending(CSSession* cs, uint32_t bitrate); |
170 | int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height); | 170 | int cs_enable_audio_receiving(CSSession* cs); |
171 | int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate); | 171 | int cs_enable_video_sending(CSSession* cs, uint32_t bitrate); |
172 | int cs_enable_video_receiving(CSSession* cs); | ||
172 | 173 | ||
174 | void cs_disable_audio_sending(CSSession* cs); | ||
175 | void cs_disable_audio_receiving(CSSession* cs); | ||
176 | void cs_disable_video_sending(CSSession* cs); | ||
177 | void cs_disable_video_receiving(CSSession* cs); | ||
173 | 178 | ||
174 | /* Internal. Called from rtp_handle_message */ | 179 | /* Internal. Called from rtp_handle_message */ |
175 | void queue_message(RTPSession *session, RTPMessage *msg); | 180 | void queue_message(RTPSession *session, RTPMessage *msg); |
diff --git a/toxav/msi.c b/toxav/msi.c index 497af13b..3de686cc 100644 --- a/toxav/msi.c +++ b/toxav/msi.c | |||
@@ -840,7 +840,7 @@ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage * | |||
840 | 840 | ||
841 | if ( call ) { | 841 | if ( call ) { |
842 | if ( call->peers[0] == (uint32_t)msg->friend_id ) { | 842 | if ( call->peers[0] == (uint32_t)msg->friend_id ) { |
843 | if (call->state == msi_CallInviting) { | 843 | if (call->state == msi_CallRequesting) { |
844 | /* The glare case. A calls B when at the same time | 844 | /* The glare case. A calls B when at the same time |
845 | * B calls A. Who has advantage is set bey calculating | 845 | * B calls A. Who has advantage is set bey calculating |
846 | * 'bigger' Call id and then that call id is being used in | 846 | * 'bigger' Call id and then that call id is being used in |
@@ -898,7 +898,7 @@ static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage * | |||
898 | } | 898 | } |
899 | 899 | ||
900 | memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) ); | 900 | memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) ); |
901 | call->state = msi_CallStarting; | 901 | call->state = msi_CallRequested; |
902 | 902 | ||
903 | add_peer( call, msg->friend_id); | 903 | add_peer( call, msg->friend_id); |
904 | flush_peer_csettings ( call, msg, 0 ); | 904 | flush_peer_csettings ( call, msg, 0 ); |
@@ -1009,7 +1009,7 @@ static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage | |||
1009 | 1009 | ||
1010 | invoke_callback(session, call->call_idx, msi_OnSelfCSChange); | 1010 | invoke_callback(session, call->call_idx, msi_OnSelfCSChange); |
1011 | 1011 | ||
1012 | } else if ( call->state == msi_CallInviting ) { | 1012 | } else if ( call->state == msi_CallRequesting ) { |
1013 | LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx ); | 1013 | LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx ); |
1014 | 1014 | ||
1015 | call->state = msi_CallActive; | 1015 | call->state = msi_CallActive; |
@@ -1344,7 +1344,7 @@ int msi_invite ( MSISession *session, | |||
1344 | send_message ( session, call, msg_invite, friend_id ); | 1344 | send_message ( session, call, msg_invite, friend_id ); |
1345 | free( msg_invite ); | 1345 | free( msg_invite ); |
1346 | 1346 | ||
1347 | call->state = msi_CallInviting; | 1347 | call->state = msi_CallRequesting; |
1348 | 1348 | ||
1349 | call->request_timer_id = timer_alloc ( session, handle_timeout, call->call_idx, m_deftout ); | 1349 | call->request_timer_id = timer_alloc ( session, handle_timeout, call->call_idx, m_deftout ); |
1350 | 1350 | ||
@@ -1402,7 +1402,7 @@ int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *cs | |||
1402 | return msi_ErrorNoCall; | 1402 | return msi_ErrorNoCall; |
1403 | } | 1403 | } |
1404 | 1404 | ||
1405 | if ( session->calls[call_index]->state != msi_CallStarting ) { | 1405 | if ( session->calls[call_index]->state != msi_CallRequested ) { |
1406 | LOGGER_ERROR("Call is in invalid state!"); | 1406 | LOGGER_ERROR("Call is in invalid state!"); |
1407 | pthread_mutex_unlock(session->mutex); | 1407 | pthread_mutex_unlock(session->mutex); |
1408 | return msi_ErrorInvalidState; | 1408 | return msi_ErrorInvalidState; |
@@ -1434,7 +1434,7 @@ int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const c | |||
1434 | return msi_ErrorNoCall; | 1434 | return msi_ErrorNoCall; |
1435 | } | 1435 | } |
1436 | 1436 | ||
1437 | if ( session->calls[call_index]->state != msi_CallInviting ) { | 1437 | if ( session->calls[call_index]->state != msi_CallRequesting ) { |
1438 | LOGGER_ERROR("Call is in invalid state!"); | 1438 | LOGGER_ERROR("Call is in invalid state!"); |
1439 | pthread_mutex_unlock(session->mutex); | 1439 | pthread_mutex_unlock(session->mutex); |
1440 | return msi_ErrorInvalidState; | 1440 | return msi_ErrorInvalidState; |
@@ -1477,7 +1477,7 @@ int msi_reject ( MSISession *session, int32_t call_index, const char *reason ) | |||
1477 | return msi_ErrorNoCall; | 1477 | return msi_ErrorNoCall; |
1478 | } | 1478 | } |
1479 | 1479 | ||
1480 | if ( session->calls[call_index]->state != msi_CallStarting ) { | 1480 | if ( session->calls[call_index]->state != msi_CallRequested ) { |
1481 | LOGGER_ERROR("Call is in invalid state!"); | 1481 | LOGGER_ERROR("Call is in invalid state!"); |
1482 | pthread_mutex_unlock(session->mutex); | 1482 | pthread_mutex_unlock(session->mutex); |
1483 | return msi_ErrorInvalidState; | 1483 | return msi_ErrorInvalidState; |
diff --git a/toxav/msi.h b/toxav/msi.h index 29d44ccc..bdd72e49 100644 --- a/toxav/msi.h +++ b/toxav/msi.h | |||
@@ -45,8 +45,8 @@ typedef enum { | |||
45 | * Call state identifiers. | 45 | * Call state identifiers. |
46 | */ | 46 | */ |
47 | typedef enum { | 47 | typedef enum { |
48 | msi_CallInviting, /* when sending call invite */ | 48 | msi_CallRequesting, /* when sending call invite */ |
49 | msi_CallStarting, /* when getting call invite */ | 49 | msi_CallRequested, /* when getting call invite */ |
50 | msi_CallActive, | 50 | msi_CallActive, |
51 | msi_CallHold, | 51 | msi_CallHold, |
52 | msi_CallOver | 52 | msi_CallOver |
diff --git a/toxav/rtp.c b/toxav/rtp.c index 2af89ebf..ba93e781 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c | |||
@@ -422,44 +422,6 @@ RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t | |||
422 | 422 | ||
423 | 423 | ||
424 | 424 | ||
425 | int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ) | ||
426 | { | ||
427 | RTPMessage *msg = rtp_new_message (session, data, length); | ||
428 | |||
429 | if ( !msg ) return -1; | ||
430 | |||
431 | if ( -1 == send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length) ) { | ||
432 | LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); | ||
433 | rtp_free_msg ( session, msg ); | ||
434 | return rtp_ErrorSending; | ||
435 | } | ||
436 | |||
437 | |||
438 | /* Set sequ number */ | ||
439 | session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1; | ||
440 | rtp_free_msg ( session, msg ); | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) | ||
446 | { | ||
447 | if ( !session ) { | ||
448 | if ( msg->ext_header ) { | ||
449 | free ( msg->ext_header->table ); | ||
450 | free ( msg->ext_header ); | ||
451 | } | ||
452 | } else { | ||
453 | if ( msg->ext_header && session->ext_header != msg->ext_header ) { | ||
454 | free ( msg->ext_header->table ); | ||
455 | free ( msg->ext_header ); | ||
456 | } | ||
457 | } | ||
458 | |||
459 | free ( msg->header ); | ||
460 | free ( msg ); | ||
461 | } | ||
462 | |||
463 | RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) | 425 | RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) |
464 | { | 426 | { |
465 | RTPSession *retu = calloc(1, sizeof(RTPSession)); | 427 | RTPSession *retu = calloc(1, sizeof(RTPSession)); |
@@ -469,16 +431,10 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) | |||
469 | return NULL; | 431 | return NULL; |
470 | } | 432 | } |
471 | 433 | ||
472 | if ( -1 == custom_lossy_packet_registerhandler(messenger, friend_num, payload_type, rtp_handle_packet, retu)) { | ||
473 | LOGGER_ERROR("Error setting custom register handler for rtp session"); | ||
474 | free(retu); | ||
475 | return NULL; | ||
476 | } | ||
477 | |||
478 | LOGGER_DEBUG("Registered packet handler: pt: %d; fid: %d", payload_type, friend_num); | 434 | LOGGER_DEBUG("Registered packet handler: pt: %d; fid: %d", payload_type, friend_num); |
479 | 435 | ||
480 | retu->version = RTP_VERSION; /* It's always 2 */ | 436 | retu->version = RTP_VERSION; /* It's always 2 */ |
481 | retu->padding = 0; /* If some additional data is needed about the packet */ | 437 | retu->padding = 0; /* If some additional data is needed about the packet */ |
482 | retu->extension = 0; /* If extension to header is needed */ | 438 | retu->extension = 0; /* If extension to header is needed */ |
483 | retu->cc = 1; /* Amount of contributors */ | 439 | retu->cc = 1; /* Amount of contributors */ |
484 | retu->csrc = NULL; /* Container */ | 440 | retu->csrc = NULL; /* Container */ |
@@ -498,23 +454,24 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) | |||
498 | free(retu); | 454 | free(retu); |
499 | return NULL; | 455 | return NULL; |
500 | } | 456 | } |
501 | 457 | ||
502 | retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */ | 458 | retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */ |
503 | 459 | ||
504 | /* Also set payload type as prefix */ | 460 | /* Also set payload type as prefix */ |
505 | retu->prefix = payload_type; | 461 | retu->prefix = payload_type; |
506 | 462 | ||
463 | retu->m = messenger; | ||
507 | /* | 464 | /* |
508 | * | 465 | * |
509 | */ | 466 | */ |
510 | return retu; | 467 | return retu; |
511 | } | 468 | } |
512 | 469 | ||
513 | void rtp_kill ( RTPSession *session, Messenger *messenger ) | 470 | void rtp_kill ( RTPSession *session ) |
514 | { | 471 | { |
515 | if ( !session ) return; | 472 | if ( !session ) return; |
516 | 473 | ||
517 | custom_lossy_packet_registerhandler(messenger, session->dest, session->prefix, NULL, NULL); | 474 | custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL); |
518 | 475 | ||
519 | free ( session->ext_header ); | 476 | free ( session->ext_header ); |
520 | free ( session->csrc ); | 477 | free ( session->csrc ); |
@@ -523,5 +480,48 @@ void rtp_kill ( RTPSession *session, Messenger *messenger ) | |||
523 | 480 | ||
524 | /* And finally free session */ | 481 | /* And finally free session */ |
525 | free ( session ); | 482 | free ( session ); |
483 | } | ||
526 | 484 | ||
485 | int rtp_register_for_receiving(RTPSession* session) | ||
486 | { | ||
487 | return custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, | ||
488 | rtp_handle_packet, session); | ||
489 | } | ||
490 | |||
491 | int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ) | ||
492 | { | ||
493 | RTPMessage *msg = rtp_new_message (session, data, length); | ||
494 | |||
495 | if ( !msg ) return -1; | ||
496 | |||
497 | if ( -1 == send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length) ) { | ||
498 | LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); | ||
499 | rtp_free_msg ( session, msg ); | ||
500 | return rtp_ErrorSending; | ||
501 | } | ||
502 | |||
503 | |||
504 | /* Set sequ number */ | ||
505 | session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1; | ||
506 | rtp_free_msg ( session, msg ); | ||
507 | |||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) | ||
512 | { | ||
513 | if ( !session ) { | ||
514 | if ( msg->ext_header ) { | ||
515 | free ( msg->ext_header->table ); | ||
516 | free ( msg->ext_header ); | ||
517 | } | ||
518 | } else { | ||
519 | if ( msg->ext_header && session->ext_header != msg->ext_header ) { | ||
520 | free ( msg->ext_header->table ); | ||
521 | free ( msg->ext_header ); | ||
522 | } | ||
523 | } | ||
524 | |||
525 | free ( msg->header ); | ||
526 | free ( msg ); | ||
527 | } | 527 | } |
diff --git a/toxav/rtp.h b/toxav/rtp.h index c98840ac..b25b13ba 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h | |||
@@ -99,6 +99,7 @@ typedef struct _RTPSession { | |||
99 | int dest; | 99 | int dest; |
100 | 100 | ||
101 | struct _CSSession *cs; | 101 | struct _CSSession *cs; |
102 | Messenger* m; | ||
102 | 103 | ||
103 | } RTPSession; | 104 | } RTPSession; |
104 | 105 | ||
@@ -110,7 +111,12 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ); | |||
110 | /** | 111 | /** |
111 | * Terminate the session. | 112 | * Terminate the session. |
112 | */ | 113 | */ |
113 | void rtp_kill ( RTPSession *session, Messenger *messenger ); | 114 | void rtp_kill ( RTPSession* session ); |
115 | |||
116 | /** | ||
117 | * By default rtp is not in receiving state | ||
118 | */ | ||
119 | int rtp_register_for_receiving (RTPSession *session); | ||
114 | 120 | ||
115 | /** | 121 | /** |
116 | * Sends msg to _RTPSession::dest | 122 | * Sends msg to _RTPSession::dest |
diff --git a/toxav/toxav.c b/toxav/toxav.c index b0534ec5..68402020 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c | |||
@@ -460,7 +460,7 @@ int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, in | |||
460 | return av_ErrorInvalidState; | 460 | return av_ErrorInvalidState; |
461 | } | 461 | } |
462 | 462 | ||
463 | if (cs_set_video_encoder_resolution(call->cs, input->d_w, input->d_h) < 0) { | 463 | if (cs_set_sending_video_resolution(call->cs, input->d_w, input->d_h) < 0) { |
464 | pthread_mutex_unlock(call->mutex_control); | 464 | pthread_mutex_unlock(call->mutex_control); |
465 | return av_ErrorSettingVideoResolution; | 465 | return av_ErrorSettingVideoResolution; |
466 | } | 466 | } |
@@ -468,7 +468,7 @@ int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, in | |||
468 | pthread_mutex_lock(call->mutex_encoding_video); | 468 | pthread_mutex_lock(call->mutex_encoding_video); |
469 | pthread_mutex_unlock(call->mutex_control); | 469 | pthread_mutex_unlock(call->mutex_control); |
470 | 470 | ||
471 | int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); | 471 | int rc = vpx_codec_encode(call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); |
472 | 472 | ||
473 | if ( rc != VPX_CODEC_OK) { | 473 | if ( rc != VPX_CODEC_OK) { |
474 | LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); | 474 | LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); |
@@ -482,7 +482,7 @@ int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, in | |||
482 | const vpx_codec_cx_pkt_t *pkt; | 482 | const vpx_codec_cx_pkt_t *pkt; |
483 | int copied = 0; | 483 | int copied = 0; |
484 | 484 | ||
485 | while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { | 485 | while ( (pkt = vpx_codec_get_cx_data(call->cs->v_encoder, &iter)) ) { |
486 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { | 486 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { |
487 | if ( copied + pkt->data.frame.sz > dest_max ) { | 487 | if ( copied + pkt->data.frame.sz > dest_max ) { |
488 | pthread_mutex_unlock(call->mutex_encoding_video); | 488 | pthread_mutex_unlock(call->mutex_encoding_video); |
@@ -608,8 +608,6 @@ ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index) | |||
608 | 608 | ||
609 | int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) | 609 | int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) |
610 | { | 610 | { |
611 | return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (CSCapabilities) capability : 0; | ||
612 | /* 0 is error here */ | ||
613 | } | 611 | } |
614 | 612 | ||
615 | Tox *toxav_get_tox(ToxAv *av) | 613 | Tox *toxav_get_tox(ToxAv *av) |
diff --git a/toxav/toxav_new.c b/toxav/toxav_new.c new file mode 100644 index 00000000..d6c1872c --- /dev/null +++ b/toxav/toxav_new.c | |||
@@ -0,0 +1,710 @@ | |||
1 | /** toxav.c | ||
2 | * | ||
3 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
4 | * | ||
5 | * This file is part of Tox. | ||
6 | * | ||
7 | * Tox is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 3 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * Tox is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include "config.h" | ||
24 | #endif /* HAVE_CONFIG_H */ | ||
25 | |||
26 | #include "toxav_new.h" | ||
27 | #include "msi.h" /* Includes codec.h and rtp.h */ | ||
28 | |||
29 | #include "../toxcore/Messenger.h" | ||
30 | #include "../toxcore/logger.h" | ||
31 | #include "../toxcore/util.h" | ||
32 | |||
33 | #include <assert.h> | ||
34 | #include <stdlib.h> | ||
35 | #include <string.h> | ||
36 | |||
37 | |||
38 | enum { | ||
39 | audio_index, | ||
40 | video_index, | ||
41 | }; | ||
42 | |||
43 | typedef struct iToxAVCall | ||
44 | { | ||
45 | pthread_mutex_t mutex_control[1]; | ||
46 | pthread_mutex_t mutex_encoding_audio[1]; | ||
47 | pthread_mutex_t mutex_encoding_video[1]; | ||
48 | pthread_mutex_t mutex_do[1]; | ||
49 | RTPSession *rtps[2]; /** Audio is first and video is second */ | ||
50 | CSSession *cs; | ||
51 | bool active; | ||
52 | int32_t friend_number; | ||
53 | int32_t call_idx; /* FIXME msi compat, remove */ | ||
54 | |||
55 | struct iToxAVCall *prev; | ||
56 | struct iToxAVCall *next; | ||
57 | } IToxAVCall; | ||
58 | |||
59 | struct toxAV | ||
60 | { | ||
61 | Messenger* m; | ||
62 | MSISession* msi; | ||
63 | |||
64 | /* Two-way storage: first is array of calls and second is list of calls with head and tail */ | ||
65 | IToxAVCall** calls; | ||
66 | uint32_t calls_tail; | ||
67 | uint32_t calls_head; | ||
68 | |||
69 | PAIR(toxav_call_cb *, void*) ccb; /* Call callback */ | ||
70 | PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */ | ||
71 | PAIR(toxav_receive_audio_frame_cb *, void *) acb; /* Audio frame receive callback */ | ||
72 | PAIR(toxav_receive_video_frame_cb *, void *) vcb; /* Video frame receive callback */ | ||
73 | |||
74 | /** Decode time measures */ | ||
75 | int32_t dmssc; /** Measure count */ | ||
76 | int32_t dmsst; /** Last cycle total */ | ||
77 | int32_t dmssa; /** Average decoding time in ms */ | ||
78 | }; | ||
79 | |||
80 | |||
81 | void i_toxav_msi_callback_invite(void* toxav_inst, int32_t call_idx, void *data); | ||
82 | void i_toxav_msi_callback_ringing(void* toxav_inst, int32_t call_idx, void *data); | ||
83 | void i_toxav_msi_callback_start(void* toxav_inst, int32_t call_idx, void *data); | ||
84 | void i_toxav_msi_callback_cancel(void* toxav_inst, int32_t call_idx, void *data); | ||
85 | void i_toxav_msi_callback_reject(void* toxav_inst, int32_t call_idx, void *data); | ||
86 | void i_toxav_msi_callback_end(void* toxav_inst, int32_t call_idx, void *data); | ||
87 | void i_toxav_msi_callback_request_to(void* toxav_inst, int32_t call_idx, void *data); /* TODO remove */ | ||
88 | void i_toxav_msi_callback_peer_to(void* toxav_inst, int32_t call_idx, void *data); | ||
89 | void i_toxav_msi_callback_state_change(void* toxav_inst, int32_t call_idx, void *data); | ||
90 | |||
91 | IToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number); | ||
92 | IToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number); | ||
93 | void i_toxav_remove_call(ToxAV* av, uint32_t friend_number); | ||
94 | bool i_toxav_audio_bitrate_invalid(uint32_t bitrate); | ||
95 | bool i_toxav_video_bitrate_invalid(uint32_t bitrate); | ||
96 | IToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error); | ||
97 | bool i_toxav_prepare_transmission(ToxAV* av, IToxAVCall* call); | ||
98 | void i_toxav_kill_transmission(ToxAV* av, IToxAVCall* call); | ||
99 | |||
100 | |||
101 | |||
102 | ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) | ||
103 | { | ||
104 | TOXAV_ERR_NEW rc = TOXAV_ERR_NEW_OK; | ||
105 | ToxAV *av = NULL; | ||
106 | |||
107 | if (tox == NULL) { | ||
108 | rc = TOXAV_ERR_NEW_NULL; | ||
109 | goto FAILURE; | ||
110 | } | ||
111 | |||
112 | if (((Messenger*)tox)->msi_packet) { | ||
113 | rc = TOXAV_ERR_NEW_MULTIPLE; | ||
114 | goto FAILURE; | ||
115 | } | ||
116 | |||
117 | av = calloc ( sizeof(ToxAV), 1); | ||
118 | |||
119 | if (av == NULL) { | ||
120 | LOGGER_WARNING("Allocation failed!"); | ||
121 | rc = TOXAV_ERR_NEW_MALLOC; | ||
122 | goto FAILURE; | ||
123 | } | ||
124 | |||
125 | av->m = (Messenger *)tox; | ||
126 | av->msi = msi_new(av->m, 100); /* TODO remove max calls */ | ||
127 | |||
128 | if (av->msi == NULL) { | ||
129 | rc = TOXAV_ERR_NEW_MALLOC; | ||
130 | goto FAILURE; | ||
131 | } | ||
132 | |||
133 | av->msi->agent_handler = av; | ||
134 | |||
135 | msi_register_callback(av->msi, i_toxav_msi_callback_invite, msi_OnInvite, NULL); | ||
136 | msi_register_callback(av->msi, i_toxav_msi_callback_ringing, msi_OnRinging, NULL); | ||
137 | msi_register_callback(av->msi, i_toxav_msi_callback_start, msi_OnStart, NULL); | ||
138 | msi_register_callback(av->msi, i_toxav_msi_callback_cancel, msi_OnCancel, NULL); | ||
139 | msi_register_callback(av->msi, i_toxav_msi_callback_reject, msi_OnReject, NULL); | ||
140 | msi_register_callback(av->msi, i_toxav_msi_callback_end, msi_OnEnd, NULL); | ||
141 | msi_register_callback(av->msi, i_toxav_msi_callback_request_to, msi_OnRequestTimeout, NULL); | ||
142 | msi_register_callback(av->msi, i_toxav_msi_callback_peer_to, msi_OnPeerTimeout, NULL); | ||
143 | msi_register_callback(av->msi, i_toxav_msi_callback_state_change, msi_OnPeerCSChange, NULL); | ||
144 | msi_register_callback(av->msi, i_toxav_msi_callback_state_change, msi_OnSelfCSChange, NULL); | ||
145 | |||
146 | |||
147 | if (error) | ||
148 | *error = rc; | ||
149 | |||
150 | return av; | ||
151 | |||
152 | FAILURE: | ||
153 | if (error) | ||
154 | *error = rc; | ||
155 | |||
156 | free(av); | ||
157 | |||
158 | return NULL; | ||
159 | } | ||
160 | |||
161 | void toxav_kill(ToxAV* av) | ||
162 | { | ||
163 | if (av == NULL) | ||
164 | return; | ||
165 | |||
166 | msi_kill(av->msi); | ||
167 | /* TODO iterate over calls */ | ||
168 | free(av); | ||
169 | } | ||
170 | |||
171 | Tox* toxav_get_tox(ToxAV* av) | ||
172 | { | ||
173 | return (Tox*) av->m; | ||
174 | } | ||
175 | |||
176 | uint32_t toxav_iteration_interval(const ToxAV* av) | ||
177 | { | ||
178 | |||
179 | } | ||
180 | |||
181 | void toxav_iteration(ToxAV* av) | ||
182 | { | ||
183 | |||
184 | } | ||
185 | |||
186 | bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) | ||
187 | { | ||
188 | IToxAVCall* call = i_toxav_init_call(av, friend_number, audio_bit_rate, video_bit_rate, error); | ||
189 | if (call == NULL) { | ||
190 | return false; | ||
191 | } | ||
192 | |||
193 | /* TODO remove csettings */ | ||
194 | MSICSettings csets; | ||
195 | csets.audio_bitrate = audio_bit_rate; | ||
196 | csets.video_bitrate = video_bit_rate; | ||
197 | |||
198 | csets.call_type = video_bit_rate ? msi_TypeVideo : msi_TypeAudio; | ||
199 | |||
200 | if (msi_invite(av->msi, &call->call_idx, &csets, 1000, friend_number) != 0) { | ||
201 | i_toxav_remove_call(av, friend_number); | ||
202 | if (error) | ||
203 | *error = TOXAV_ERR_CALL_MALLOC; /* FIXME: this should be the only reason to fail */ | ||
204 | return false; | ||
205 | } | ||
206 | |||
207 | return true; | ||
208 | } | ||
209 | |||
210 | void toxav_callback_call(ToxAV* av, toxav_call_cb* function, void* user_data) | ||
211 | { | ||
212 | av->ccb.first = function; | ||
213 | av->ccb.second = user_data; | ||
214 | } | ||
215 | |||
216 | bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER* error) | ||
217 | { | ||
218 | TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK; | ||
219 | if (m_friend_exists(av->m, friend_number)) { | ||
220 | rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND; | ||
221 | goto END; | ||
222 | } | ||
223 | |||
224 | if ((audio_bit_rate && i_toxav_audio_bitrate_invalid(audio_bit_rate)) | ||
225 | ||(video_bit_rate && i_toxav_video_bitrate_invalid(video_bit_rate)) | ||
226 | ) { | ||
227 | rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; | ||
228 | goto END; | ||
229 | } | ||
230 | |||
231 | IToxAVCall* call = i_toxav_get_call(av, friend_number); | ||
232 | if (call == NULL || av->msi->calls[call->call_idx]->state != msi_CallRequested) { | ||
233 | rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; | ||
234 | goto END; | ||
235 | } | ||
236 | |||
237 | /* TODO remove csettings */ | ||
238 | MSICSettings csets; | ||
239 | csets.audio_bitrate = audio_bit_rate; | ||
240 | csets.video_bitrate = video_bit_rate; | ||
241 | |||
242 | csets.call_type = video_bit_rate ? msi_TypeVideo : msi_TypeAudio; | ||
243 | |||
244 | if (msi_answer(av->msi, call->call_idx, &csets) != 0) { | ||
245 | rc = TOXAV_ERR_ANSWER_MALLOC; /* TODO Some error here */ | ||
246 | /* TODO Reject call? */ | ||
247 | } | ||
248 | |||
249 | END: | ||
250 | if (error) | ||
251 | *error = rc; | ||
252 | |||
253 | return rc == TOXAV_ERR_ANSWER_OK; | ||
254 | } | ||
255 | |||
256 | void toxav_callback_call_state(ToxAV* av, toxav_call_state_cb* function, void* user_data) | ||
257 | { | ||
258 | av->scb.first = function; | ||
259 | av->scb.second = user_data; | ||
260 | } | ||
261 | |||
262 | bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL* error) | ||
263 | { | ||
264 | |||
265 | } | ||
266 | |||
267 | bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, TOXAV_ERR_BIT_RATE* error) | ||
268 | { | ||
269 | |||
270 | } | ||
271 | |||
272 | bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_bit_rate, TOXAV_ERR_BIT_RATE* error) | ||
273 | { | ||
274 | |||
275 | } | ||
276 | |||
277 | void toxav_callback_request_video_frame(ToxAV* av, toxav_request_video_frame_cb* function, void* user_data) | ||
278 | { | ||
279 | |||
280 | } | ||
281 | |||
282 | bool 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, const uint8_t* a, TOXAV_ERR_SEND_FRAME* error) | ||
283 | { | ||
284 | |||
285 | } | ||
286 | |||
287 | void toxav_callback_request_audio_frame(ToxAV* av, toxav_request_audio_frame_cb* function, void* user_data) | ||
288 | { | ||
289 | |||
290 | } | ||
291 | |||
292 | bool 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) | ||
293 | { | ||
294 | |||
295 | } | ||
296 | |||
297 | void toxav_callback_receive_video_frame(ToxAV* av, toxav_receive_video_frame_cb* function, void* user_data) | ||
298 | { | ||
299 | |||
300 | } | ||
301 | |||
302 | void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb* function, void* user_data) | ||
303 | { | ||
304 | |||
305 | } | ||
306 | |||
307 | |||
308 | /******************************************************************************* | ||
309 | * | ||
310 | * :: Internal | ||
311 | * | ||
312 | ******************************************************************************/ | ||
313 | /** TODO: | ||
314 | * - In msi call_idx can be the same as friend id | ||
315 | * - If crutial callback not present send error | ||
316 | * - Remove *data from msi | ||
317 | * - Remove CSettings from msi | ||
318 | */ | ||
319 | void i_toxav_msi_callback_invite(void* toxav_inst, int32_t call_idx, void* data) | ||
320 | { | ||
321 | ToxAV* toxav = toxav_inst; | ||
322 | |||
323 | uint32_t ab = toxav->msi->calls[call_idx]->csettings_peer[0].audio_bitrate; | ||
324 | uint32_t vb = toxav->msi->calls[call_idx]->csettings_peer[0].video_bitrate; | ||
325 | |||
326 | IToxAVCall* call = i_toxav_init_call(toxav, toxav->msi->calls[call_idx]->peers[0], ab, vb, NULL); | ||
327 | if (call == NULL) { | ||
328 | msi_reject(toxav->msi, call_idx, NULL); | ||
329 | return false; | ||
330 | } | ||
331 | |||
332 | call->call_idx = call_idx; | ||
333 | |||
334 | if (toxav->ccb.first) | ||
335 | toxav->ccb.first(toxav, toxav->msi->calls[call_idx]->peers[0], true, true, toxav->ccb.second); | ||
336 | } | ||
337 | |||
338 | void i_toxav_msi_callback_ringing(void* toxav_inst, int32_t call_idx, void* data) | ||
339 | { | ||
340 | ToxAV* toxav = toxav_inst; | ||
341 | if (toxav->scb.first) | ||
342 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
343 | TOXAV_CALL_STATE_RINGING, toxav->scb.second); | ||
344 | } | ||
345 | |||
346 | void i_toxav_msi_callback_start(void* toxav_inst, int32_t call_idx, void* data) | ||
347 | { | ||
348 | ToxAV* toxav = toxav_inst; | ||
349 | |||
350 | IToxAVCall* call = i_toxav_get_call(toxav, toxav->msi->calls[call_idx]->peers[0]); | ||
351 | |||
352 | if (call == NULL || !i_toxav_prepare_transmission(toxav, call)) { | ||
353 | /* TODO send error */ | ||
354 | i_toxav_remove_call(toxav, toxav->msi->calls[call_idx]->peers[0]); | ||
355 | return; | ||
356 | } | ||
357 | |||
358 | TOXAV_CALL_STATE state; | ||
359 | const MSICSettings* csets = toxav->msi->calls[call_idx]->csettings_peer[0]; | ||
360 | |||
361 | if (csets->audio_bitrate && csets->video_bitrate) | ||
362 | state = TOXAV_CALL_STATE_SENDING_AV; | ||
363 | else if (csets->video_bitrate == 0) | ||
364 | state = TOXAV_CALL_STATE_SENDING_A; | ||
365 | else | ||
366 | state = TOXAV_CALL_STATE_SENDING_V; | ||
367 | |||
368 | if (toxav->scb.first) /* TODO this */ | ||
369 | toxav->scb.first(toxav, call->friend_number, state, toxav->scb.second); | ||
370 | } | ||
371 | |||
372 | void i_toxav_msi_callback_cancel(void* toxav_inst, int32_t call_idx, void* data) | ||
373 | { | ||
374 | ToxAV* toxav = toxav_inst; | ||
375 | if (toxav->scb.first) | ||
376 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
377 | TOXAV_CALL_STATE_END, toxav->scb.second); | ||
378 | } | ||
379 | |||
380 | void i_toxav_msi_callback_reject(void* toxav_inst, int32_t call_idx, void* data) | ||
381 | { | ||
382 | ToxAV* toxav = toxav_inst; | ||
383 | if (toxav->scb.first) | ||
384 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
385 | TOXAV_CALL_STATE_END, toxav->scb.second); | ||
386 | } | ||
387 | |||
388 | void i_toxav_msi_callback_end(void* toxav_inst, int32_t call_idx, void* data) | ||
389 | { | ||
390 | ToxAV* toxav = toxav_inst; | ||
391 | if (toxav->scb.first) | ||
392 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
393 | TOXAV_CALL_STATE_END, toxav->scb.second); | ||
394 | } | ||
395 | |||
396 | void i_toxav_msi_callback_request_to(void* toxav_inst, int32_t call_idx, void* data) | ||
397 | { | ||
398 | /* TODO remove */ | ||
399 | ToxAV* toxav = toxav_inst; | ||
400 | if (toxav->scb.first) | ||
401 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
402 | TOXAV_CALL_STATE_ERROR, toxav->scb.second); | ||
403 | } | ||
404 | |||
405 | void i_toxav_msi_callback_peer_to(void* toxav_inst, int32_t call_idx, void* data) | ||
406 | { | ||
407 | ToxAV* toxav = toxav_inst; | ||
408 | if (toxav->scb.first) | ||
409 | toxav->scb.first(toxav, toxav->msi->calls[call_idx]->peers[0], | ||
410 | TOXAV_CALL_STATE_ERROR, toxav->scb.second); | ||
411 | } | ||
412 | |||
413 | void i_toxav_msi_callback_state_change(void* toxav_inst, int32_t call_idx, void* data) | ||
414 | { | ||
415 | ToxAV* toxav = toxav_inst; | ||
416 | /* TODO something something msi */ | ||
417 | } | ||
418 | |||
419 | IToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number) | ||
420 | { | ||
421 | if (av->calls_tail < friend_number) | ||
422 | return NULL; | ||
423 | |||
424 | return av->calls[friend_number]; | ||
425 | } | ||
426 | |||
427 | IToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number) | ||
428 | { | ||
429 | IToxAVCall* rc = calloc(sizeof(IToxAVCall), 1); | ||
430 | |||
431 | if (rc == NULL) | ||
432 | return NULL; | ||
433 | |||
434 | rc->friend_number = friend_number; | ||
435 | |||
436 | if (create_recursive_mutex(rc->mutex_control) != 0) { | ||
437 | free(rc); | ||
438 | return NULL; | ||
439 | } | ||
440 | |||
441 | if (create_recursive_mutex(rc->mutex_do) != 0) { | ||
442 | pthread_mutex_destroy(rc->mutex_control); | ||
443 | free(rc); | ||
444 | return NULL; | ||
445 | } | ||
446 | |||
447 | |||
448 | if (av->calls == NULL) { /* Creating */ | ||
449 | av->calls = calloc (sizeof(IToxAVCall*), friend_number + 1); | ||
450 | |||
451 | if (av->calls == NULL) { | ||
452 | pthread_mutex_destroy(rc->mutex_control); | ||
453 | pthread_mutex_destroy(rc->mutex_do); | ||
454 | free(rc); | ||
455 | return NULL; | ||
456 | } | ||
457 | |||
458 | av->calls_tail = av->calls_head = friend_number; | ||
459 | |||
460 | } else if (av->calls_tail < friend_number) { /* Appending */ | ||
461 | void* tmp = realloc(av->calls, sizeof(IToxAVCall*) * friend_number + 1); | ||
462 | |||
463 | if (tmp == NULL) { | ||
464 | pthread_mutex_destroy(rc->mutex_control); | ||
465 | pthread_mutex_destroy(rc->mutex_do); | ||
466 | free(rc); | ||
467 | return NULL; | ||
468 | } | ||
469 | |||
470 | av->calls = tmp; | ||
471 | |||
472 | /* Set fields in between to null */ | ||
473 | int32_t i = av->calls_tail; | ||
474 | for (; i < friend_number; i ++) | ||
475 | av->calls[i] = NULL; | ||
476 | |||
477 | rc->prev = av->calls[av->calls_tail]; | ||
478 | av->calls[av->calls_tail]->next = rc; | ||
479 | |||
480 | av->calls_tail = friend_number; | ||
481 | |||
482 | } else if (av->calls_head > friend_number) { /* Inserting at front */ | ||
483 | rc->next = av->calls[av->calls_head]; | ||
484 | av->calls[av->calls_head]->prev = rc; | ||
485 | av->calls_head = friend_number; | ||
486 | } | ||
487 | |||
488 | av->calls[friend_number] = rc; | ||
489 | return rc; | ||
490 | } | ||
491 | |||
492 | void i_toxav_remove_call(ToxAV* av, uint32_t friend_number) | ||
493 | { | ||
494 | IToxAVCall* tc = i_toxav_get_call(av, friend_number); | ||
495 | |||
496 | if (tc == NULL) | ||
497 | return; | ||
498 | |||
499 | IToxAVCall* prev = tc->prev; | ||
500 | IToxAVCall* next = tc->next; | ||
501 | |||
502 | pthread_mutex_destroy(tc->mutex_control); | ||
503 | pthread_mutex_destroy(tc->mutex_do); | ||
504 | |||
505 | free(tc); | ||
506 | |||
507 | if (prev) | ||
508 | prev->next = next; | ||
509 | else if (next) | ||
510 | av->calls_head = next->friend_number; | ||
511 | else goto CLEAR; | ||
512 | |||
513 | if (next) | ||
514 | next->prev = prev; | ||
515 | else if (prev) | ||
516 | av->calls_tail = prev->friend_number; | ||
517 | else goto CLEAR; | ||
518 | |||
519 | av->calls[friend_number] = NULL; | ||
520 | return; | ||
521 | |||
522 | CLEAR: | ||
523 | av->calls_head = av->calls_tail = 0; | ||
524 | free(av->calls); | ||
525 | av->calls = NULL; | ||
526 | } | ||
527 | |||
528 | bool i_toxav_audio_bitrate_invalid(uint32_t bitrate) | ||
529 | { | ||
530 | /* Opus RFC 6716 section-2.1.1 dictates the following: | ||
531 | * Opus supports all bitrates from 6 kbit/s to 510 kbit/s. | ||
532 | */ | ||
533 | return bitrate < 6 || bitrate > 510; | ||
534 | } | ||
535 | |||
536 | bool i_toxav_video_bitrate_invalid(uint32_t bitrate) | ||
537 | { | ||
538 | /* TODO: If anyone knows the answer to this one please fill it up */ | ||
539 | return false; | ||
540 | } | ||
541 | |||
542 | IToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) | ||
543 | { | ||
544 | TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK; | ||
545 | IToxAVCall* call = NULL; | ||
546 | |||
547 | if (m_friend_exists(av->m, friend_number)) { | ||
548 | rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND; | ||
549 | goto END; | ||
550 | } | ||
551 | |||
552 | if (m_get_friend_connectionstatus(av->m, friend_number) != 1) { | ||
553 | rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED; | ||
554 | goto END; | ||
555 | } | ||
556 | |||
557 | if (i_toxav_get_call(av, friend_number) != NULL) { | ||
558 | rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL; | ||
559 | goto END; | ||
560 | } | ||
561 | |||
562 | if ((audio_bit_rate && i_toxav_audio_bitrate_invalid(audio_bit_rate)) | ||
563 | ||(video_bit_rate && i_toxav_video_bitrate_invalid(video_bit_rate)) | ||
564 | ) { | ||
565 | rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; | ||
566 | goto END; | ||
567 | } | ||
568 | |||
569 | IToxAVCall* call = i_toxav_add_call(av, friend_number); | ||
570 | if (call == NULL) { | ||
571 | rc = TOXAV_ERR_CALL_MALLOC; | ||
572 | } | ||
573 | |||
574 | END: | ||
575 | if (error) | ||
576 | *error = rc; | ||
577 | |||
578 | return call; | ||
579 | } | ||
580 | |||
581 | bool i_toxav_prepare_transmission(ToxAV* av, IToxAVCall* call) | ||
582 | { | ||
583 | pthread_mutex_lock(call->mutex_control); | ||
584 | |||
585 | if (call->active) { | ||
586 | pthread_mutex_unlock(call->mutex_control); | ||
587 | LOGGER_WARNING("Call already active!\n"); | ||
588 | return true; | ||
589 | } | ||
590 | |||
591 | if (pthread_mutex_init(call->mutex_encoding_audio, NULL) != 0) | ||
592 | goto MUTEX_INIT_ERROR; | ||
593 | |||
594 | if (pthread_mutex_init(call->mutex_encoding_video, NULL) != 0) { | ||
595 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
596 | goto MUTEX_INIT_ERROR; | ||
597 | } | ||
598 | |||
599 | if (pthread_mutex_init(call->mutex_do, NULL) != 0) { | ||
600 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
601 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
602 | goto MUTEX_INIT_ERROR; | ||
603 | } | ||
604 | |||
605 | const MSICSettings *c_peer = &av->msi->calls[call->call_idx]->csettings_peer[0]; | ||
606 | const MSICSettings *c_self = &av->msi->calls[call->call_idx]->csettings_local; | ||
607 | |||
608 | call->cs = cs_new(c_self->audio_bitrate, c_peer->audio_bitrate, | ||
609 | c_self->video_bitrate, c_peer->video_bitrate); | ||
610 | |||
611 | if ( !call->cs ) { | ||
612 | LOGGER_ERROR("Error while starting Codec State!\n"); | ||
613 | goto FAILURE; | ||
614 | } | ||
615 | |||
616 | call->cs->agent = av; | ||
617 | call->cs->call_idx = call->call_idx; | ||
618 | |||
619 | call->cs->acb.first = av->acb.first; | ||
620 | call->cs->acb.second = av->acb.second; | ||
621 | |||
622 | call->cs->vcb.first = av->vcb.first; | ||
623 | call->cs->vcb.second = av->vcb.second; | ||
624 | |||
625 | |||
626 | if (c_self->audio_bitrate > 0 || c_peer->audio_bitrate > 0) { /* Prepare audio rtp */ | ||
627 | call->rtps[audio_index] = rtp_new(msi_TypeAudio, av->m, av->msi->calls[call->call_idx]->peers[0]); | ||
628 | |||
629 | if ( !call->rtps[audio_index] ) { | ||
630 | LOGGER_ERROR("Error while starting audio RTP session!\n"); | ||
631 | goto FAILURE; | ||
632 | } | ||
633 | |||
634 | call->rtps[audio_index]->cs = call->cs; | ||
635 | |||
636 | if (c_peer->audio_bitrate > 0) | ||
637 | rtp_register_for_receiving(call->rtps[audio_index]); | ||
638 | } | ||
639 | |||
640 | if (c_self->video_bitrate > 0 || c_peer->video_bitrate > 0) { /* Prepare video rtp */ | ||
641 | call->rtps[video_index] = rtp_new(msi_TypeVideo, av->m, av->msi->calls[call->call_idx]->peers[0]); | ||
642 | |||
643 | if ( !call->rtps[video_index] ) { | ||
644 | LOGGER_ERROR("Error while starting video RTP session!\n"); | ||
645 | goto FAILURE; | ||
646 | } | ||
647 | |||
648 | call->rtps[video_index]->cs = call->cs; | ||
649 | |||
650 | if (c_peer->video_bitrate > 0) | ||
651 | rtp_register_for_receiving(call->rtps[audio_index]); | ||
652 | } | ||
653 | |||
654 | call->active = 1; | ||
655 | pthread_mutex_unlock(call->mutex_control); | ||
656 | return true; | ||
657 | |||
658 | FAILURE: | ||
659 | rtp_kill(call->rtps[audio_index]); | ||
660 | call->rtps[audio_index] = NULL; | ||
661 | rtp_kill(call->rtps[video_index]); | ||
662 | call->rtps[video_index] = NULL; | ||
663 | cs_kill(call->cs); | ||
664 | call->cs = NULL; | ||
665 | call->active = 0; | ||
666 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
667 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
668 | pthread_mutex_destroy(call->mutex_do); | ||
669 | |||
670 | pthread_mutex_unlock(call->mutex_control); | ||
671 | return false; | ||
672 | |||
673 | MUTEX_INIT_ERROR: | ||
674 | pthread_mutex_unlock(call->mutex_control); | ||
675 | LOGGER_ERROR("Mutex initialization failed!\n"); | ||
676 | return false; | ||
677 | } | ||
678 | |||
679 | void i_toxav_kill_transmission(ToxAV* av, IToxAVCall* call) | ||
680 | { | ||
681 | pthread_mutex_lock(call->mutex_control); | ||
682 | |||
683 | if (!call->active) { | ||
684 | pthread_mutex_unlock(call->mutex_control); | ||
685 | LOGGER_WARNING("Action on inactive call: %d", call->call_idx); | ||
686 | return; | ||
687 | } | ||
688 | |||
689 | call->active = 0; | ||
690 | |||
691 | pthread_mutex_lock(call->mutex_encoding_audio); | ||
692 | pthread_mutex_unlock(call->mutex_encoding_audio); | ||
693 | pthread_mutex_lock(call->mutex_encoding_video); | ||
694 | pthread_mutex_unlock(call->mutex_encoding_video); | ||
695 | pthread_mutex_lock(call->mutex_do); | ||
696 | pthread_mutex_unlock(call->mutex_do); | ||
697 | |||
698 | rtp_kill(call->rtps[audio_index]); | ||
699 | call->rtps[audio_index] = NULL; | ||
700 | rtp_kill(call->rtps[video_index]); | ||
701 | call->rtps[video_index] = NULL; | ||
702 | cs_kill(call->cs); | ||
703 | call->cs = NULL; | ||
704 | |||
705 | pthread_mutex_destroy(call->mutex_encoding_audio); | ||
706 | pthread_mutex_destroy(call->mutex_encoding_video); | ||
707 | pthread_mutex_destroy(call->mutex_do); | ||
708 | |||
709 | pthread_mutex_unlock(call->mutex_control); | ||
710 | } | ||
diff --git a/toxav/toxav_new.h b/toxav/toxav_new.h new file mode 100644 index 00000000..78e79357 --- /dev/null +++ b/toxav/toxav_new.h | |||
@@ -0,0 +1,481 @@ | |||
1 | #pragma once | ||
2 | #include <stdbool.h> | ||
3 | #include <stddef.h> | ||
4 | #include <stdint.h> | ||
5 | /** \page av Public audio/video API for Tox clients. | ||
6 | * | ||
7 | * Unlike the Core API, this API is fully thread-safe. The library will ensure | ||
8 | * the proper synchronisation of parallel calls. | ||
9 | */ | ||
10 | /** | ||
11 | * The type of the Tox Audio/Video subsystem object. | ||
12 | */ | ||
13 | typedef struct toxAV ToxAV; | ||
14 | #ifndef TOX_DEFINED | ||
15 | #define TOX_DEFINED | ||
16 | /** | ||
17 | * The type of a Tox instance. Repeated here so this file does not have a direct | ||
18 | * dependency on the Core interface. | ||
19 | */ | ||
20 | typedef struct Tox Tox; | ||
21 | #endif | ||
22 | /******************************************************************************* | ||
23 | * | ||
24 | * :: Creation and destruction | ||
25 | * | ||
26 | ******************************************************************************/ | ||
27 | typedef enum TOXAV_ERR_NEW { | ||
28 | TOXAV_ERR_NEW_OK, | ||
29 | TOXAV_ERR_NEW_NULL, | ||
30 | /** | ||
31 | * Memory allocation failure while trying to allocate structures required for | ||
32 | * the A/V session. | ||
33 | */ | ||
34 | TOXAV_ERR_NEW_MALLOC, | ||
35 | /** | ||
36 | * Attempted to create a second session for the same Tox instance. | ||
37 | */ | ||
38 | TOXAV_ERR_NEW_MULTIPLE | ||
39 | } TOXAV_ERR_NEW; | ||
40 | /** | ||
41 | * Start new A/V session. There can only be only one session per Tox instance. | ||
42 | */ | ||
43 | ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error); | ||
44 | /** | ||
45 | * Releases all resources associated with the A/V session. | ||
46 | * | ||
47 | * If any calls were ongoing, these will be forcibly terminated without | ||
48 | * notifying peers. After calling this function, no other functions may be | ||
49 | * called and the av pointer becomes invalid. | ||
50 | */ | ||
51 | void toxav_kill(ToxAV *av); | ||
52 | /** | ||
53 | * Returns the Tox instance the A/V object was created for. | ||
54 | */ | ||
55 | Tox *toxav_get_tox(ToxAV *av); | ||
56 | /******************************************************************************* | ||
57 | * | ||
58 | * :: A/V event loop | ||
59 | * | ||
60 | ******************************************************************************/ | ||
61 | /** | ||
62 | * Returns the interval in milliseconds when the next toxav_iteration should be | ||
63 | * called. If no call is active at the moment, this function returns 200. | ||
64 | */ | ||
65 | uint32_t toxav_iteration_interval(ToxAV const *av); | ||
66 | /** | ||
67 | * Main loop for the session. This function needs to be called in intervals of | ||
68 | * toxav_iteration_interval() milliseconds. It is best called in the same loop | ||
69 | * as tox_iteration. | ||
70 | */ | ||
71 | void toxav_iteration(ToxAV *av); | ||
72 | /******************************************************************************* | ||
73 | * | ||
74 | * :: Call setup | ||
75 | * | ||
76 | ******************************************************************************/ | ||
77 | typedef enum TOXAV_ERR_CALL { | ||
78 | TOXAV_ERR_CALL_OK, | ||
79 | /** | ||
80 | * A resource allocation error occurred while trying to create the structures | ||
81 | * required for the call. | ||
82 | */ | ||
83 | TOXAV_ERR_CALL_MALLOC, | ||
84 | /** | ||
85 | * The friend number did not designate a valid friend. | ||
86 | */ | ||
87 | TOXAV_ERR_CALL_FRIEND_NOT_FOUND, | ||
88 | /** | ||
89 | * The friend was valid, but not currently connected. | ||
90 | */ | ||
91 | TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED, | ||
92 | /** | ||
93 | * Attempted to call a friend while already in an audio or video call with | ||
94 | * them. | ||
95 | */ | ||
96 | TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL, | ||
97 | /** | ||
98 | * Audio or video bit rate is invalid. | ||
99 | */ | ||
100 | TOXAV_ERR_CALL_INVALID_BIT_RATE | ||
101 | } TOXAV_ERR_CALL; | ||
102 | /** | ||
103 | * Call a friend. This will start ringing the friend. | ||
104 | * | ||
105 | * It is the client's responsibility to stop ringing after a certain timeout, | ||
106 | * if such behaviour is desired. If the client does not stop ringing, the A/V | ||
107 | * library will not stop until the friend is disconnected. | ||
108 | * | ||
109 | * @param friend_number The friend number of the friend that should be called. | ||
110 | * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable | ||
111 | * audio sending. | ||
112 | * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable | ||
113 | * video sending. | ||
114 | */ | ||
115 | bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL *error); | ||
116 | /** | ||
117 | * The function type for the `call` callback. | ||
118 | */ | ||
119 | typedef void toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data); | ||
120 | /** | ||
121 | * Set the callback for the `call` event. Pass NULL to unset. | ||
122 | * | ||
123 | * This event is triggered when a call is received from a friend. | ||
124 | */ | ||
125 | void toxav_callback_call(ToxAV *av, toxav_call_cb *function, void *user_data); | ||
126 | typedef enum TOXAV_ERR_ANSWER { | ||
127 | TOXAV_ERR_ANSWER_OK, | ||
128 | /** | ||
129 | * A resource allocation error occurred while trying to create the structures | ||
130 | * required for the call. | ||
131 | */ | ||
132 | TOXAV_ERR_ANSWER_MALLOC, | ||
133 | /** | ||
134 | * The friend number did not designate a valid friend. | ||
135 | */ | ||
136 | TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND, | ||
137 | /** | ||
138 | * The friend was valid, but they are not currently trying to initiate a call. | ||
139 | * This is also returned if this client is already in a call with the friend. | ||
140 | */ | ||
141 | TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING, | ||
142 | /** | ||
143 | * Audio or video bit rate is invalid. | ||
144 | */ | ||
145 | TOXAV_ERR_ANSWER_INVALID_BIT_RATE | ||
146 | } TOXAV_ERR_ANSWER; | ||
147 | /** | ||
148 | * Accept an incoming call. | ||
149 | * | ||
150 | * If an allocation error occurs while answering a call, both participants will | ||
151 | * receive TOXAV_CALL_STATE_ERROR and the call will end. | ||
152 | * | ||
153 | * @param friend_number The friend number of the friend that is calling. | ||
154 | * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable | ||
155 | * audio sending. | ||
156 | * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable | ||
157 | * video sending. | ||
158 | */ | ||
159 | bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER *error); | ||
160 | /******************************************************************************* | ||
161 | * | ||
162 | * :: Call state graph | ||
163 | * | ||
164 | ******************************************************************************/ | ||
165 | typedef enum TOXAV_CALL_STATE { | ||
166 | /** | ||
167 | * The friend's client is aware of the call. This happens after calling | ||
168 | * toxav_call and the initial call request has been received. | ||
169 | */ | ||
170 | TOXAV_CALL_STATE_RINGING, | ||
171 | /** | ||
172 | * Not sending anything. Either the friend requested that this client stops | ||
173 | * sending anything, or the client turned off both audio and video by setting | ||
174 | * the respective bit rates to 0. | ||
175 | * | ||
176 | * If both sides are in this state, the call is effectively on hold, but not | ||
177 | * in the PAUSED state. | ||
178 | */ | ||
179 | TOXAV_CALL_STATE_NOT_SENDING, | ||
180 | /** | ||
181 | * Sending audio only. Either the friend requested that this client stops | ||
182 | * sending video, or the client turned off video by setting the video bit rate | ||
183 | * to 0. | ||
184 | */ | ||
185 | TOXAV_CALL_STATE_SENDING_A, | ||
186 | /** | ||
187 | * Sending video only. Either the friend requested that this client stops | ||
188 | * sending audio (muted), or the client turned off audio by setting the audio | ||
189 | * bit rate to 0. | ||
190 | */ | ||
191 | TOXAV_CALL_STATE_SENDING_V, | ||
192 | /** | ||
193 | * Sending both audio and video. | ||
194 | */ | ||
195 | TOXAV_CALL_STATE_SENDING_AV, | ||
196 | /** | ||
197 | * The call is on hold. Both sides stop sending and receiving. | ||
198 | */ | ||
199 | TOXAV_CALL_STATE_PAUSED, | ||
200 | /** | ||
201 | * The call has finished. This is the final state after which no more state | ||
202 | * transitions can occur for the call. | ||
203 | */ | ||
204 | TOXAV_CALL_STATE_END, | ||
205 | /** | ||
206 | * Sent by the AV core if an error occurred on the remote end. | ||
207 | */ | ||
208 | TOXAV_CALL_STATE_ERROR | ||
209 | } TOXAV_CALL_STATE; | ||
210 | /** | ||
211 | * The function type for the `call_state` callback. | ||
212 | * | ||
213 | * @param friend_number The friend number for which the call state changed. | ||
214 | * @param state The new call state. | ||
215 | */ | ||
216 | typedef void toxav_call_state_cb(ToxAV *av, uint32_t friend_number, TOXAV_CALL_STATE state, void *user_data); | ||
217 | /** | ||
218 | * Set the callback for the `call_state` event. Pass NULL to unset. | ||
219 | * | ||
220 | * This event is triggered when a call state transition occurs. | ||
221 | */ | ||
222 | void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *function, void *user_data); | ||
223 | /******************************************************************************* | ||
224 | * | ||
225 | * :: Call control | ||
226 | * | ||
227 | ******************************************************************************/ | ||
228 | typedef enum TOXAV_CALL_CONTROL { | ||
229 | /** | ||
230 | * Resume a previously paused call. Only valid if the pause was caused by this | ||
231 | * client. Not valid before the call is accepted. | ||
232 | */ | ||
233 | TOXAV_CALL_CONTROL_RESUME, | ||
234 | /** | ||
235 | * Put a call on hold. Not valid before the call is accepted. | ||
236 | */ | ||
237 | TOXAV_CALL_CONTROL_PAUSE, | ||
238 | /** | ||
239 | * Reject a call if it was not answered, yet. Cancel a call after it was | ||
240 | * answered. | ||
241 | */ | ||
242 | TOXAV_CALL_CONTROL_CANCEL, | ||
243 | /** | ||
244 | * Request that the friend stops sending audio. Regardless of the friend's | ||
245 | * compliance, this will cause the `receive_audio_frame` event to stop being | ||
246 | * triggered on receiving an audio frame from the friend. | ||
247 | */ | ||
248 | TOXAV_CALL_CONTROL_MUTE_AUDIO, | ||
249 | /** | ||
250 | * Request that the friend stops sending video. Regardless of the friend's | ||
251 | * compliance, this will cause the `receive_video_frame` event to stop being | ||
252 | * triggered on receiving an video frame from the friend. | ||
253 | */ | ||
254 | TOXAV_CALL_CONTROL_MUTE_VIDEO | ||
255 | } TOXAV_CALL_CONTROL; | ||
256 | typedef enum TOXAV_ERR_CALL_CONTROL { | ||
257 | TOXAV_ERR_CALL_CONTROL_OK, | ||
258 | /** | ||
259 | * The friend_number passed did not designate a valid friend. | ||
260 | */ | ||
261 | TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND, | ||
262 | /** | ||
263 | * This client is currently not in a call with the friend. Before the call is | ||
264 | * answered, only CANCEL is a valid control. | ||
265 | */ | ||
266 | TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL, | ||
267 | /** | ||
268 | * Attempted to resume a call that was not paused. | ||
269 | */ | ||
270 | TOXAV_ERR_CALL_CONTROL_NOT_PAUSED, | ||
271 | /** | ||
272 | * Attempted to resume a call that was paused by the other party. Also set if | ||
273 | * the client attempted to send a system-only control. | ||
274 | */ | ||
275 | TOXAV_ERR_CALL_CONTROL_DENIED, | ||
276 | /** | ||
277 | * The call was already paused on this client. It is valid to pause if the | ||
278 | * other party paused the call. The call will resume after both parties sent | ||
279 | * the RESUME control. | ||
280 | */ | ||
281 | TOXAV_ERR_CALL_CONTROL_ALREADY_PAUSED | ||
282 | } TOXAV_ERR_CALL_CONTROL; | ||
283 | /** | ||
284 | * Sends a call control command to a friend. | ||
285 | * | ||
286 | * @param friend_number The friend number of the friend this client is in a call | ||
287 | * with. | ||
288 | * @param control The control command to send. | ||
289 | * | ||
290 | * @return true on success. | ||
291 | */ | ||
292 | bool toxav_call_control(ToxAV *av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error); | ||
293 | /******************************************************************************* | ||
294 | * | ||
295 | * :: Controlling bit rates | ||
296 | * | ||
297 | ******************************************************************************/ | ||
298 | typedef enum TOXAV_ERR_BIT_RATE { | ||
299 | TOXAV_ERR_BIT_RATE_OK, | ||
300 | /** | ||
301 | * The bit rate passed was not one of the supported values. | ||
302 | */ | ||
303 | TOXAV_ERR_BIT_RATE_INVALID | ||
304 | } TOXAV_ERR_BIT_RATE; | ||
305 | /** | ||
306 | * Set the audio bit rate to be used in subsequent audio frames. | ||
307 | * | ||
308 | * @param friend_number The friend number of the friend for which to set the | ||
309 | * audio bit rate. | ||
310 | * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable | ||
311 | * audio sending. | ||
312 | * | ||
313 | * @see toxav_call for the valid bit rates. | ||
314 | */ | ||
315 | bool toxav_set_audio_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, TOXAV_ERR_BIT_RATE *error); | ||
316 | /** | ||
317 | * Set the video bit rate to be used in subsequent video frames. | ||
318 | * | ||
319 | * @param friend_number The friend number of the friend for which to set the | ||
320 | * video bit rate. | ||
321 | * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable | ||
322 | * video sending. | ||
323 | * | ||
324 | * @see toxav_call for the valid bit rates. | ||
325 | */ | ||
326 | bool toxav_set_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, TOXAV_ERR_BIT_RATE *error); | ||
327 | /******************************************************************************* | ||
328 | * | ||
329 | * :: A/V sending | ||
330 | * | ||
331 | ******************************************************************************/ | ||
332 | /** | ||
333 | * Common error codes for the send_*_frame functions. | ||
334 | */ | ||
335 | typedef enum TOXAV_ERR_SEND_FRAME { | ||
336 | TOXAV_ERR_SEND_FRAME_OK, | ||
337 | /** | ||
338 | * In case of video, one of Y, U, or V was NULL. In case of audio, the samples | ||
339 | * data pointer was NULL. | ||
340 | */ | ||
341 | TOXAV_ERR_SEND_FRAME_NULL, | ||
342 | /** | ||
343 | * The friend_number passed did not designate a valid friend. | ||
344 | */ | ||
345 | TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND, | ||
346 | /** | ||
347 | * This client is currently not in a call with the friend. | ||
348 | */ | ||
349 | TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL, | ||
350 | /** | ||
351 | * No video frame had been requested through the `request_video_frame` event, | ||
352 | * but the client tried to send one, anyway. | ||
353 | */ | ||
354 | TOXAV_ERR_SEND_FRAME_NOT_REQUESTED, | ||
355 | /** | ||
356 | * One of the frame parameters was invalid. E.g. the resolution may be too | ||
357 | * small or too large, or the audio sampling rate may be unsupported. | ||
358 | */ | ||
359 | TOXAV_ERR_SEND_FRAME_INVALID | ||
360 | } TOXAV_ERR_SEND_FRAME; | ||
361 | /** | ||
362 | * The function type for the `request_video_frame` callback. | ||
363 | * | ||
364 | * @param friend_number The friend number of the friend for which the next video | ||
365 | * frame should be sent. | ||
366 | */ | ||
367 | typedef void toxav_request_video_frame_cb(ToxAV *av, uint32_t friend_number, void *user_data); | ||
368 | /** | ||
369 | * Set the callback for the `request_video_frame` event. Pass NULL to unset. | ||
370 | */ | ||
371 | void toxav_callback_request_video_frame(ToxAV *av, toxav_request_video_frame_cb *function, void *user_data); | ||
372 | /** | ||
373 | * Send a video frame to a friend. | ||
374 | * | ||
375 | * This is called in response to receiving the `request_video_frame` event. | ||
376 | * | ||
377 | * Each plane should contain (width * height) pixels. The Alpha plane can be | ||
378 | * NULL, in which case every pixel is assumed fully opaque. | ||
379 | * | ||
380 | * @param friend_number The friend number of the friend to which to send a video | ||
381 | * frame. | ||
382 | * @param width Width of the frame in pixels. | ||
383 | * @param height Height of the frame in pixels. | ||
384 | * @param y Y (Luminance) plane data. | ||
385 | * @param u U (Chroma) plane data. | ||
386 | * @param v V (Chroma) plane data. | ||
387 | * @param a A (Alpha) plane data. | ||
388 | */ | ||
389 | bool toxav_send_video_frame(ToxAV *av, uint32_t friend_number, | ||
390 | uint16_t width, uint16_t height, | ||
391 | uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a, | ||
392 | TOXAV_ERR_SEND_FRAME *error); | ||
393 | /** | ||
394 | * The function type for the `request_audio_frame` callback. | ||
395 | * | ||
396 | * @param friend_number The friend number of the friend for which the next audio | ||
397 | * frame should be sent. | ||
398 | */ | ||
399 | typedef void toxav_request_audio_frame_cb(ToxAV *av, uint32_t friend_number, void *user_data); | ||
400 | /** | ||
401 | * Set the callback for the `request_audio_frame` event. Pass NULL to unset. | ||
402 | */ | ||
403 | void toxav_callback_request_audio_frame(ToxAV *av, toxav_request_audio_frame_cb *function, void *user_data); | ||
404 | /** | ||
405 | * Send an audio frame to a friend. | ||
406 | * | ||
407 | * This is called in response to receiving the `request_audio_frame` event. | ||
408 | * | ||
409 | * The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]... | ||
410 | * Meaning: sample 1 for channel 1, sample 1 for channel 2, ... | ||
411 | * For mono audio, this has no meaning, every sample is subsequent. For stereo, | ||
412 | * this means the expected format is LRLRLR... with samples for left and right | ||
413 | * alternating. | ||
414 | * | ||
415 | * @param friend_number The friend number of the friend to which to send an | ||
416 | * audio frame. | ||
417 | * @param pcm An array of audio samples. The size of this array must be | ||
418 | * sample_count * channels. | ||
419 | * @param sample_count Number of samples in this frame. Valid numbers here are | ||
420 | * ((sample rate) * (audio length) / 1000), where audio length can be | ||
421 | * 2.5, 5, 10, 20, 40 or 60 millseconds. | ||
422 | * @param channels Number of audio channels. Must be at least 1 for mono. | ||
423 | * For voice over IP, more than 2 channels (stereo) typically doesn't make | ||
424 | * sense, but up to 255 channels are supported. | ||
425 | * @param sampling_rate Audio sampling rate used in this frame. Valid sampling | ||
426 | * rates are 8000, 12000, 16000, 24000, or 48000. | ||
427 | */ | ||
428 | bool toxav_send_audio_frame(ToxAV *av, uint32_t friend_number, | ||
429 | int16_t const *pcm, | ||
430 | size_t sample_count, | ||
431 | uint8_t channels, | ||
432 | uint32_t sampling_rate, | ||
433 | TOXAV_ERR_SEND_FRAME *error); | ||
434 | /******************************************************************************* | ||
435 | * | ||
436 | * :: A/V receiving | ||
437 | * | ||
438 | ******************************************************************************/ | ||
439 | /** | ||
440 | * The function type for the `receive_video_frame` callback. | ||
441 | * | ||
442 | * Each plane contains (width * height) pixels. The Alpha plane can be NULL, in | ||
443 | * which case every pixel should be assumed fully opaque. | ||
444 | * | ||
445 | * @param friend_number The friend number of the friend who sent a video frame. | ||
446 | * @param width Width of the frame in pixels. | ||
447 | * @param height Height of the frame in pixels. | ||
448 | * @param y Y (Luminance) plane data. | ||
449 | * @param u U (Chroma) plane data. | ||
450 | * @param v V (Chroma) plane data. | ||
451 | * @param a A (Alpha) plane data. | ||
452 | */ | ||
453 | typedef void toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, | ||
454 | uint16_t width, uint16_t height, | ||
455 | uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a, | ||
456 | void *user_data); | ||
457 | /** | ||
458 | * Set the callback for the `receive_video_frame` event. Pass NULL to unset. | ||
459 | */ | ||
460 | void toxav_callback_receive_video_frame(ToxAV *av, toxav_receive_video_frame_cb *function, void *user_data); | ||
461 | /** | ||
462 | * The function type for the `receive_audio_frame` callback. | ||
463 | * | ||
464 | * @param friend_number The friend number of the friend who sent an audio frame. | ||
465 | * @param pcm An array of audio samples (sample_count * channels elements). | ||
466 | * @param sample_count The number of audio samples per channel in the PCM array. | ||
467 | * @param channels Number of audio channels. | ||
468 | * @param sampling_rate Sampling rate used in this frame. | ||
469 | * | ||
470 | * @see toxav_send_audio_frame for the audio format. | ||
471 | */ | ||
472 | typedef void toxav_receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, | ||
473 | int16_t const *pcm, | ||
474 | size_t sample_count, | ||
475 | uint8_t channels, | ||
476 | uint32_t sampling_rate, | ||
477 | void *user_data); | ||
478 | /** | ||
479 | * Set the callback for the `receive_audio_frame` event. Pass NULL to unset. | ||
480 | */ | ||
481 | void toxav_callback_receive_audio_frame(ToxAV *av, toxav_receive_audio_frame_cb *function, void *user_data); \ No newline at end of file | ||
diff --git a/toxcore/logger.c b/toxcore/logger.c index 2ef5f21a..ac81a900 100644 --- a/toxcore/logger.c +++ b/toxcore/logger.c | |||
@@ -42,7 +42,7 @@ | |||
42 | #endif | 42 | #endif |
43 | 43 | ||
44 | 44 | ||
45 | typedef struct logger { | 45 | struct logger { |
46 | FILE *log_file; | 46 | FILE *log_file; |
47 | LOG_LEVEL level; | 47 | LOG_LEVEL level; |
48 | uint64_t start_time; /* Time when lib loaded */ | 48 | uint64_t start_time; /* Time when lib loaded */ |
@@ -55,7 +55,7 @@ typedef struct logger { | |||
55 | 55 | ||
56 | /* For thread synchronisation */ | 56 | /* For thread synchronisation */ |
57 | pthread_mutex_t mutex[1]; | 57 | pthread_mutex_t mutex[1]; |
58 | } logger; | 58 | }; |
59 | 59 | ||
60 | logger *global = NULL; | 60 | logger *global = NULL; |
61 | 61 | ||
diff --git a/toxcore/util.h b/toxcore/util.h index 7cd6bb8b..fab26e29 100644 --- a/toxcore/util.h +++ b/toxcore/util.h | |||
@@ -53,6 +53,7 @@ typedef int (*load_state_callback_func)(void *outer, const uint8_t *data, uint32 | |||
53 | int load_state(load_state_callback_func load_state_callback, void *outer, | 53 | int load_state(load_state_callback_func load_state_callback, void *outer, |
54 | const uint8_t *data, uint32_t length, uint16_t cookie_inner); | 54 | const uint8_t *data, uint32_t length, uint16_t cookie_inner); |
55 | 55 | ||
56 | /* Returns -1 if failed or 0 if success */ | ||
56 | int create_recursive_mutex(pthread_mutex_t *mutex); | 57 | int create_recursive_mutex(pthread_mutex_t *mutex); |
57 | 58 | ||
58 | #endif /* __UTIL_H__ */ | 59 | #endif /* __UTIL_H__ */ |