diff options
author | mannol <eniz_vukovic@hotmail.com> | 2015-04-13 01:45:53 +0200 |
---|---|---|
committer | mannol <eniz_vukovic@hotmail.com> | 2015-04-13 01:45:53 +0200 |
commit | 2465f486acd90ed8395c8a83a13af09ecd024c98 (patch) | |
tree | 4abe53d39eb07a45e5ed4d8894b7ae038e2bb705 /toxav | |
parent | b2d88a4544a81a217db18b60d91a44d85821db3d (diff) |
Started custom RTCP
Diffstat (limited to 'toxav')
-rw-r--r-- | toxav/av_test.c | 107 | ||||
-rw-r--r-- | toxav/codec.c | 98 | ||||
-rw-r--r-- | toxav/codec.h | 8 | ||||
-rw-r--r-- | toxav/msi.h | 4 | ||||
-rw-r--r-- | toxav/rtp.c | 589 | ||||
-rw-r--r-- | toxav/rtp.h | 49 | ||||
-rw-r--r-- | toxav/toxav.c | 3 |
7 files changed, 414 insertions, 444 deletions
diff --git a/toxav/av_test.c b/toxav/av_test.c index dab1f6ef..c20d459b 100644 --- a/toxav/av_test.c +++ b/toxav/av_test.c | |||
@@ -1,5 +1,31 @@ | |||
1 | /** av_test.c | ||
2 | * | ||
3 | * Copyright (C) 2013-2015 Tox project All Rights Reserved. | ||
4 | * | ||
5 | * This file is part of Tox. | ||
6 | * | ||
7 | * Tox is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 3 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * Tox is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | * Compile with (Linux only; in newly created directory toxcore/dir_name): | ||
21 | * gcc -o av_test ../toxav/av_test.c ../build/.libs/libtox*.a -lopencv_core \ | ||
22 | * -lopencv_highgui -lopencv_imgproc -lsndfile -pthread -lvpx -lopus -lsodium -lportaudio | ||
23 | */ | ||
24 | |||
25 | |||
1 | #include "../toxav/toxav.h" | 26 | #include "../toxav/toxav.h" |
2 | #include "../toxcore/tox.h" | 27 | #include "../toxcore/tox.h" |
28 | #include "../toxcore/util.h" | ||
3 | 29 | ||
4 | /* Playing audio data */ | 30 | /* Playing audio data */ |
5 | #include <portaudio.h> | 31 | #include <portaudio.h> |
@@ -11,7 +37,6 @@ | |||
11 | #include <opencv/highgui.h> | 37 | #include <opencv/highgui.h> |
12 | #include <opencv/cvwimage.h> | 38 | #include <opencv/cvwimage.h> |
13 | 39 | ||
14 | |||
15 | #include <sys/stat.h> | 40 | #include <sys/stat.h> |
16 | #include <assert.h> | 41 | #include <assert.h> |
17 | #include <stdio.h> | 42 | #include <stdio.h> |
@@ -63,8 +88,8 @@ struct toxav_thread_data { | |||
63 | int32_t sig; | 88 | int32_t sig; |
64 | }; | 89 | }; |
65 | 90 | ||
66 | const char* vdout = "AV Test"; | 91 | const char* vdout = "AV Test"; /* Video output */ |
67 | PaStream* adout = NULL; | 92 | PaStream* adout = NULL; /* Audio output */ |
68 | 93 | ||
69 | const char* stringify_state(TOXAV_CALL_STATE s) | 94 | const char* stringify_state(TOXAV_CALL_STATE s) |
70 | { | 95 | { |
@@ -230,7 +255,7 @@ void* iterate_toxav (void * data) | |||
230 | toxav_iterate(data_cast->BobAV); | 255 | toxav_iterate(data_cast->BobAV); |
231 | int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV)); | 256 | int rc = MIN(toxav_iteration_interval(data_cast->AliceAV), toxav_iteration_interval(data_cast->BobAV)); |
232 | 257 | ||
233 | // cvWaitKey(rc); | 258 | // cvWaitKey(10); |
234 | c_sleep(10); | 259 | c_sleep(10); |
235 | } | 260 | } |
236 | 261 | ||
@@ -306,7 +331,26 @@ int print_help (const char* name) | |||
306 | 331 | ||
307 | int main (int argc, char** argv) | 332 | int main (int argc, char** argv) |
308 | { | 333 | { |
334 | RingBuffer* rb = rb_new(4); | ||
335 | int a[5] = {0, 1, 2, 3, 4}; | ||
336 | int* x; | ||
337 | rb_write(rb, a + 0); | ||
338 | rb_write(rb, a + 1); | ||
339 | rb_write(rb, a + 2); | ||
340 | rb_write(rb, a + 3); | ||
341 | // rb_write(rb, a + 4); | ||
342 | |||
343 | x = rb_write(rb, a + 4); | ||
344 | while (rb_read(rb, (void**) &x)) | ||
345 | // rb_read(rb, (void**)&x); | ||
346 | printf("%d ", *x); | ||
347 | |||
348 | printf("\n"); | ||
349 | // int r = 43; | ||
350 | // printf("%d\n", r >= 40 ? 3 : r / 10); | ||
351 | return 0; | ||
309 | Pa_Initialize(); | 352 | Pa_Initialize(); |
353 | |||
310 | struct stat st; | 354 | struct stat st; |
311 | 355 | ||
312 | /* AV files for testing */ | 356 | /* AV files for testing */ |
@@ -395,53 +439,6 @@ int main (int argc, char** argv) | |||
395 | return 1; | 439 | return 1; |
396 | } | 440 | } |
397 | 441 | ||
398 | if (0) { | ||
399 | SNDFILE* af_handle; | ||
400 | SF_INFO af_info; | ||
401 | |||
402 | /* Open audio file */ | ||
403 | af_handle = sf_open(af_name, SFM_READ, &af_info); | ||
404 | if (af_handle == NULL) { | ||
405 | printf("Failed to open the file.\n"); | ||
406 | exit(1); | ||
407 | } | ||
408 | |||
409 | int frame_size = (af_info.samplerate * audio_frame_duration / 1000) * af_info.channels; | ||
410 | |||
411 | struct PaStreamParameters output; | ||
412 | output.device = audio_out_dev_idx; /* default output device */ | ||
413 | output.channelCount = af_info.channels; | ||
414 | output.sampleFormat = paInt16; | ||
415 | output.suggestedLatency = audio_dev->defaultHighOutputLatency; | ||
416 | output.hostApiSpecificStreamInfo = NULL; | ||
417 | |||
418 | |||
419 | PaError err = Pa_OpenStream(&adout, NULL, &output, af_info.samplerate, frame_size, paNoFlag, NULL, NULL); | ||
420 | assert(err == paNoError); | ||
421 | |||
422 | err = Pa_StartStream(adout); | ||
423 | assert(err == paNoError); | ||
424 | |||
425 | int16_t PCM[frame_size]; | ||
426 | |||
427 | time_t start_time = time(NULL); | ||
428 | time_t expected_time = af_info.frames / af_info.samplerate + 2; | ||
429 | |||
430 | printf("Sample rate %d\n", af_info.samplerate); | ||
431 | while ( start_time + expected_time > time(NULL) ) { | ||
432 | |||
433 | int64_t count = sf_read_short(af_handle, PCM, frame_size); | ||
434 | if (count > 0) { | ||
435 | t_toxav_receive_audio_frame_cb(NULL, 0, PCM, count, af_info.channels, af_info.samplerate, NULL); | ||
436 | } | ||
437 | |||
438 | c_sleep(audio_frame_duration / 2); | ||
439 | } | ||
440 | |||
441 | Pa_Terminate(); | ||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | printf("Using audio device: %s\n", audio_dev->name); | 442 | printf("Using audio device: %s\n", audio_dev->name); |
446 | printf("Using audio file: %s\n", af_name); | 443 | printf("Using audio file: %s\n", af_name); |
447 | printf("Using video file: %s\n", vf_name); | 444 | printf("Using video file: %s\n", vf_name); |
@@ -758,17 +755,15 @@ int main (int argc, char** argv) | |||
758 | 755 | ||
759 | printf("Sample rate %d\n", af_info.samplerate); | 756 | printf("Sample rate %d\n", af_info.samplerate); |
760 | while ( start_time + expected_time > time(NULL) ) { | 757 | while ( start_time + expected_time > time(NULL) ) { |
761 | |||
762 | int64_t count = sf_read_short(af_handle, PCM, frame_size); | 758 | int64_t count = sf_read_short(af_handle, PCM, frame_size); |
763 | if (count > 0) { | 759 | if (count > 0) { |
764 | TOXAV_ERR_SEND_FRAME rc; | 760 | TOXAV_ERR_SEND_FRAME rc; |
765 | if (toxav_send_audio_frame(AliceAV, 0, PCM, count/af_info.channels, af_info.channels, af_info.samplerate, &rc) == false) { | 761 | if (toxav_send_audio_frame(AliceAV, 0, PCM, count/af_info.channels, af_info.channels, af_info.samplerate, &rc) == false) { |
766 | printf("Error sending frame of size %ld: %d\n", count, rc); | 762 | printf("Error sending frame of size %ld: %d\n", count, rc); |
767 | // exit(1); | ||
768 | } | 763 | } |
769 | } | 764 | } |
770 | iterate_tox(bootstrap, AliceAV, BobAV); | 765 | iterate_tox(bootstrap, AliceAV, BobAV); |
771 | c_sleep(30); | 766 | c_sleep(53); |
772 | } | 767 | } |
773 | 768 | ||
774 | 769 | ||
@@ -794,6 +789,8 @@ int main (int argc, char** argv) | |||
794 | while(data.sig != 1) | 789 | while(data.sig != 1) |
795 | pthread_yield(); | 790 | pthread_yield(); |
796 | 791 | ||
792 | Pa_StopStream(adout); | ||
793 | |||
797 | printf("Success!"); | 794 | printf("Success!"); |
798 | } | 795 | } |
799 | 796 | ||
@@ -890,5 +887,7 @@ int main (int argc, char** argv) | |||
890 | tox_kill(bootstrap); | 887 | tox_kill(bootstrap); |
891 | 888 | ||
892 | printf("\nTest successful!\n"); | 889 | printf("\nTest successful!\n"); |
890 | |||
891 | Pa_Terminate(); | ||
893 | return 0; | 892 | return 0; |
894 | } | 893 | } |
diff --git a/toxav/codec.c b/toxav/codec.c index d55cc345..671be1ac 100644 --- a/toxav/codec.c +++ b/toxav/codec.c | |||
@@ -46,78 +46,13 @@ | |||
46 | #define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ | 46 | #define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */ |
47 | #define VIDEOFRAME_HEADER_SIZE 0x2 | 47 | #define VIDEOFRAME_HEADER_SIZE 0x2 |
48 | 48 | ||
49 | /* FIXME: Might not be enough */ | 49 | /* FIXME: Might not be enough? NOTE: I think it is enough */ |
50 | #define VIDEO_DECODE_BUFFER_SIZE 20 | 50 | #define VIDEO_DECODE_BUFFER_SIZE 20 |
51 | 51 | ||
52 | #define ARRAY(TYPE__) struct { uint16_t size; TYPE__ data[]; } | 52 | #define ARRAY(TYPE__) struct { uint16_t size; TYPE__ data[]; } |
53 | 53 | ||
54 | typedef ARRAY(uint8_t) Payload; | 54 | typedef ARRAY(uint8_t) Payload; |
55 | 55 | ||
56 | typedef struct { | ||
57 | uint16_t size; /* Max size */ | ||
58 | uint16_t start; | ||
59 | uint16_t end; | ||
60 | Payload **packets; | ||
61 | } PayloadBuffer; | ||
62 | |||
63 | static bool buffer_full(const PayloadBuffer *b) | ||
64 | { | ||
65 | return (b->end + 1) % b->size == b->start; | ||
66 | } | ||
67 | |||
68 | static bool buffer_empty(const PayloadBuffer *b) | ||
69 | { | ||
70 | return b->end == b->start; | ||
71 | } | ||
72 | |||
73 | static void buffer_write(PayloadBuffer *b, Payload *p) | ||
74 | { | ||
75 | b->packets[b->end] = p; | ||
76 | b->end = (b->end + 1) % b->size; | ||
77 | |||
78 | if (b->end == b->start) b->start = (b->start + 1) % b->size; /* full, overwrite */ | ||
79 | } | ||
80 | |||
81 | static void buffer_read(PayloadBuffer *b, Payload **p) | ||
82 | { | ||
83 | *p = b->packets[b->start]; | ||
84 | b->start = (b->start + 1) % b->size; | ||
85 | } | ||
86 | |||
87 | static void buffer_clear(PayloadBuffer *b) | ||
88 | { | ||
89 | while (!buffer_empty(b)) { | ||
90 | Payload *p; | ||
91 | buffer_read(b, &p); | ||
92 | free(p); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | static PayloadBuffer *buffer_new(int size) | ||
97 | { | ||
98 | PayloadBuffer *buf = calloc(sizeof(PayloadBuffer), 1); | ||
99 | |||
100 | if (!buf) return NULL; | ||
101 | |||
102 | buf->size = size + 1; /* include empty elem */ | ||
103 | |||
104 | if (!(buf->packets = calloc(buf->size, sizeof(Payload *)))) { | ||
105 | free(buf); | ||
106 | return NULL; | ||
107 | } | ||
108 | |||
109 | return buf; | ||
110 | } | ||
111 | |||
112 | static void buffer_free(PayloadBuffer *b) | ||
113 | { | ||
114 | if (b) { | ||
115 | buffer_clear(b); | ||
116 | free(b->packets); | ||
117 | free(b); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | /* JITTER BUFFER WORK */ | 56 | /* JITTER BUFFER WORK */ |
122 | typedef struct JitterBuffer_s { | 57 | typedef struct JitterBuffer_s { |
123 | RTPMessage **queue; | 58 | RTPMessage **queue; |
@@ -318,7 +253,7 @@ bool reconfigure_audio_decoder(CSession* cs, int32_t sampling_rate, int8_t chann | |||
318 | int status; | 253 | int status; |
319 | OpusDecoder* new_dec = opus_decoder_create(sampling_rate, channels, &status ); | 254 | OpusDecoder* new_dec = opus_decoder_create(sampling_rate, channels, &status ); |
320 | if ( status != OPUS_OK ) { | 255 | if ( status != OPUS_OK ) { |
321 | LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status)); | 256 | LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status)); |
322 | return false; | 257 | return false; |
323 | } | 258 | } |
324 | 259 | ||
@@ -336,7 +271,6 @@ bool reconfigure_audio_decoder(CSession* cs, int32_t sampling_rate, int8_t chann | |||
336 | } | 271 | } |
337 | 272 | ||
338 | /* PUBLIC */ | 273 | /* PUBLIC */ |
339 | |||
340 | void cs_do(CSession *cs) | 274 | void cs_do(CSession *cs) |
341 | { | 275 | { |
342 | /* Codec session should always be protected by call mutex so no need to check for cs validity | 276 | /* Codec session should always be protected by call mutex so no need to check for cs validity |
@@ -416,9 +350,9 @@ void cs_do(CSession *cs) | |||
416 | } | 350 | } |
417 | 351 | ||
418 | /********************* VIDEO *********************/ | 352 | /********************* VIDEO *********************/ |
419 | if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) { | 353 | if (cs->vbuf_raw && !rb_empty(cs->vbuf_raw)) { |
420 | /* Decode video */ | 354 | /* Decode video */ |
421 | buffer_read(cs->vbuf_raw, &p); | 355 | rb_read(cs->vbuf_raw, (void**)&p); |
422 | 356 | ||
423 | /* Leave space for (possibly) other thread to queue more data after we read it here */ | 357 | /* Leave space for (possibly) other thread to queue more data after we read it here */ |
424 | LOGGED_UNLOCK(cs->queue_mutex); | 358 | LOGGED_UNLOCK(cs->queue_mutex); |
@@ -447,7 +381,6 @@ void cs_do(CSession *cs) | |||
447 | 381 | ||
448 | LOGGED_UNLOCK(cs->queue_mutex); | 382 | LOGGED_UNLOCK(cs->queue_mutex); |
449 | } | 383 | } |
450 | |||
451 | CSession *cs_new(uint32_t peer_video_frame_piece_size) | 384 | CSession *cs_new(uint32_t peer_video_frame_piece_size) |
452 | { | 385 | { |
453 | CSession *cs = calloc(sizeof(CSession), 1); | 386 | CSession *cs = calloc(sizeof(CSession), 1); |
@@ -510,7 +443,7 @@ CSession *cs_new(uint32_t peer_video_frame_piece_size) | |||
510 | goto AUDIO_DECODER_CLEANUP; | 443 | goto AUDIO_DECODER_CLEANUP; |
511 | } | 444 | } |
512 | 445 | ||
513 | if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) { | 446 | if ( !(cs->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)) ) { |
514 | free(cs->frame_buf); | 447 | free(cs->frame_buf); |
515 | vpx_codec_destroy(cs->v_decoder); | 448 | vpx_codec_destroy(cs->v_decoder); |
516 | goto AUDIO_DECODER_CLEANUP; | 449 | goto AUDIO_DECODER_CLEANUP; |
@@ -542,7 +475,7 @@ CSession *cs_new(uint32_t peer_video_frame_piece_size) | |||
542 | return cs; | 475 | return cs; |
543 | 476 | ||
544 | VIDEO_DECODER_CLEANUP: | 477 | VIDEO_DECODER_CLEANUP: |
545 | buffer_free(cs->vbuf_raw); | 478 | rb_free(cs->vbuf_raw); |
546 | free(cs->frame_buf); | 479 | free(cs->frame_buf); |
547 | vpx_codec_destroy(cs->v_decoder); | 480 | vpx_codec_destroy(cs->v_decoder); |
548 | AUDIO_DECODER_CLEANUP: | 481 | AUDIO_DECODER_CLEANUP: |
@@ -553,7 +486,6 @@ FAILURE: | |||
553 | free(cs); | 486 | free(cs); |
554 | return NULL; | 487 | return NULL; |
555 | } | 488 | } |
556 | |||
557 | void cs_kill(CSession *cs) | 489 | void cs_kill(CSession *cs) |
558 | { | 490 | { |
559 | if (!cs) | 491 | if (!cs) |
@@ -567,22 +499,21 @@ void cs_kill(CSession *cs) | |||
567 | vpx_codec_destroy(cs->v_decoder); | 499 | vpx_codec_destroy(cs->v_decoder); |
568 | opus_encoder_destroy(cs->audio_encoder); | 500 | opus_encoder_destroy(cs->audio_encoder); |
569 | opus_decoder_destroy(cs->audio_decoder); | 501 | opus_decoder_destroy(cs->audio_decoder); |
570 | buffer_free(cs->vbuf_raw); | 502 | rb_free(cs->vbuf_raw); |
571 | jbuf_free(cs->j_buf); | 503 | jbuf_free(cs->j_buf); |
572 | free(cs->frame_buf); | 504 | free(cs->frame_buf); |
505 | free(cs->split_video_frame); | ||
573 | 506 | ||
574 | pthread_mutex_destroy(cs->queue_mutex); | 507 | pthread_mutex_destroy(cs->queue_mutex); |
575 | 508 | ||
576 | LOGGER_DEBUG("Terminated codec state: %p", cs); | 509 | LOGGER_DEBUG("Terminated codec state: %p", cs); |
577 | free(cs); | 510 | free(cs); |
578 | } | 511 | } |
579 | |||
580 | void cs_init_video_splitter_cycle(CSession* cs) | 512 | void cs_init_video_splitter_cycle(CSession* cs) |
581 | { | 513 | { |
582 | cs->split_video_frame[0] = cs->frameid_out++; | 514 | cs->split_video_frame[0] = cs->frameid_out++; |
583 | cs->split_video_frame[1] = 0; | 515 | cs->split_video_frame[1] = 0; |
584 | } | 516 | } |
585 | |||
586 | int cs_update_video_splitter_cycle(CSession *cs, const uint8_t *payload, uint16_t length) | 517 | int cs_update_video_splitter_cycle(CSession *cs, const uint8_t *payload, uint16_t length) |
587 | { | 518 | { |
588 | cs->processing_video_frame = payload; | 519 | cs->processing_video_frame = payload; |
@@ -590,7 +521,6 @@ int cs_update_video_splitter_cycle(CSession *cs, const uint8_t *payload, uint16_ | |||
590 | 521 | ||
591 | return ((length - 1) / VIDEOFRAME_PIECE_SIZE) + 1; | 522 | return ((length - 1) / VIDEOFRAME_PIECE_SIZE) + 1; |
592 | } | 523 | } |
593 | |||
594 | const uint8_t *cs_iterate_split_video_frame(CSession *cs, uint16_t *size) | 524 | const uint8_t *cs_iterate_split_video_frame(CSession *cs, uint16_t *size) |
595 | { | 525 | { |
596 | if (!cs || !size) return NULL; | 526 | if (!cs || !size) return NULL; |
@@ -616,9 +546,6 @@ const uint8_t *cs_iterate_split_video_frame(CSession *cs, uint16_t *size) | |||
616 | 546 | ||
617 | return cs->split_video_frame; | 547 | return cs->split_video_frame; |
618 | } | 548 | } |
619 | |||
620 | |||
621 | |||
622 | int cs_reconfigure_video_encoder(CSession* cs, int32_t bitrate, uint16_t width, uint16_t height) | 549 | int cs_reconfigure_video_encoder(CSession* cs, int32_t bitrate, uint16_t width, uint16_t height) |
623 | { | 550 | { |
624 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; | 551 | vpx_codec_enc_cfg_t cfg = *cs->v_encoder[0].config.enc; |
@@ -637,7 +564,6 @@ int cs_reconfigure_video_encoder(CSession* cs, int32_t bitrate, uint16_t width, | |||
637 | 564 | ||
638 | return 0; | 565 | return 0; |
639 | } | 566 | } |
640 | |||
641 | int cs_reconfigure_audio_encoder(CSession* cs, int32_t bitrate, int32_t sampling_rate, uint8_t channels) | 567 | int cs_reconfigure_audio_encoder(CSession* cs, int32_t bitrate, int32_t sampling_rate, uint8_t channels) |
642 | { | 568 | { |
643 | /* Values are checked in toxav.c */ | 569 | /* Values are checked in toxav.c */ |
@@ -667,8 +593,6 @@ int cs_reconfigure_audio_encoder(CSession* cs, int32_t bitrate, int32_t sampling | |||
667 | LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bitrate, sampling_rate, channels); | 593 | LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", bitrate, sampling_rate, channels); |
668 | return 0; | 594 | return 0; |
669 | } | 595 | } |
670 | |||
671 | |||
672 | /* Called from RTP */ | 596 | /* Called from RTP */ |
673 | void queue_message(RTPSession *session, RTPMessage *msg) | 597 | void queue_message(RTPSession *session, RTPMessage *msg) |
674 | { | 598 | { |
@@ -705,10 +629,10 @@ void queue_message(RTPSession *session, RTPMessage *msg) | |||
705 | if (p) { | 629 | if (p) { |
706 | LOGGED_LOCK(cs->queue_mutex); | 630 | LOGGED_LOCK(cs->queue_mutex); |
707 | 631 | ||
708 | if (buffer_full(cs->vbuf_raw)) { | 632 | if (rb_full(cs->vbuf_raw)) { |
709 | LOGGER_DEBUG("Dropped video frame"); | 633 | LOGGER_DEBUG("Dropped video frame"); |
710 | Payload *tp; | 634 | Payload *tp; |
711 | buffer_read(cs->vbuf_raw, &tp); | 635 | rb_read(cs->vbuf_raw, (void**)&tp); |
712 | free(tp); | 636 | free(tp); |
713 | } else { | 637 | } else { |
714 | p->size = cs->frame_size; | 638 | p->size = cs->frame_size; |
@@ -720,7 +644,7 @@ void queue_message(RTPSession *session, RTPMessage *msg) | |||
720 | cs->lcfd = t_lcfd > 100 ? cs->lcfd : t_lcfd; | 644 | cs->lcfd = t_lcfd > 100 ? cs->lcfd : t_lcfd; |
721 | cs->linfts = current_time_monotonic(); | 645 | cs->linfts = current_time_monotonic(); |
722 | 646 | ||
723 | buffer_write(cs->vbuf_raw, p); | 647 | rb_write(cs->vbuf_raw, p); |
724 | LOGGED_UNLOCK(cs->queue_mutex); | 648 | LOGGED_UNLOCK(cs->queue_mutex); |
725 | } else { | 649 | } else { |
726 | LOGGER_WARNING("Allocation failed! Program might misbehave!"); | 650 | LOGGER_WARNING("Allocation failed! Program might misbehave!"); |
diff --git a/toxav/codec.h b/toxav/codec.h index 830dbbf6..7cc9b15d 100644 --- a/toxav/codec.h +++ b/toxav/codec.h | |||
@@ -25,6 +25,8 @@ | |||
25 | #include "toxav.h" | 25 | #include "toxav.h" |
26 | #include "rtp.h" | 26 | #include "rtp.h" |
27 | 27 | ||
28 | #include "../toxcore/util.h" | ||
29 | |||
28 | #include <stdio.h> | 30 | #include <stdio.h> |
29 | #include <math.h> | 31 | #include <math.h> |
30 | #include <pthread.h> | 32 | #include <pthread.h> |
@@ -40,8 +42,6 @@ | |||
40 | /* Audio encoding/decoding */ | 42 | /* Audio encoding/decoding */ |
41 | #include <opus.h> | 43 | #include <opus.h> |
42 | 44 | ||
43 | #define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; } | ||
44 | |||
45 | #define PACKED_AUDIO_SIZE(x) (x + 5) | 45 | #define PACKED_AUDIO_SIZE(x) (x + 5) |
46 | #define UNPACKED_AUDIO_SIZE(x) (x - 5) | 46 | #define UNPACKED_AUDIO_SIZE(x) (x - 5) |
47 | 47 | ||
@@ -125,8 +125,4 @@ const uint8_t *cs_iterate_split_video_frame(CSession *cs, uint16_t *size); | |||
125 | 125 | ||
126 | int cs_reconfigure_video_encoder(CSession* cs, int32_t bitrate, uint16_t width, uint16_t height); | 126 | int cs_reconfigure_video_encoder(CSession* cs, int32_t bitrate, uint16_t width, uint16_t height); |
127 | int cs_reconfigure_audio_encoder(CSession* cs, int32_t bitrate, int32_t sampling_rate, uint8_t channels); | 127 | int cs_reconfigure_audio_encoder(CSession* cs, int32_t bitrate, int32_t sampling_rate, uint8_t channels); |
128 | |||
129 | |||
130 | /* Internal. Called from rtp_handle_message */ | ||
131 | void queue_message(RTPSession *session, RTPMessage *msg); | ||
132 | #endif /* CODEC_H */ | 128 | #endif /* CODEC_H */ |
diff --git a/toxav/msi.h b/toxav/msi.h index 8404df19..7d82afc8 100644 --- a/toxav/msi.h +++ b/toxav/msi.h | |||
@@ -29,7 +29,7 @@ | |||
29 | #include "../toxcore/Messenger.h" | 29 | #include "../toxcore/Messenger.h" |
30 | 30 | ||
31 | /** Preconfigured value for video splitting */ | 31 | /** Preconfigured value for video splitting */ |
32 | #define VIDEOFRAME_PIECE_SIZE 500 /* 1.25 KiB*/ | 32 | #define VIDEOFRAME_PIECE_SIZE 500 |
33 | 33 | ||
34 | /** | 34 | /** |
35 | * Error codes. | 35 | * Error codes. |
@@ -42,7 +42,7 @@ typedef enum { | |||
42 | msi_EStrayMessage, | 42 | msi_EStrayMessage, |
43 | msi_ESystem, | 43 | msi_ESystem, |
44 | msi_EHandle, | 44 | msi_EHandle, |
45 | msi_EUndisclosed, /* NOTE: must be last enum otherwise parsing wont work */ | 45 | msi_EUndisclosed, /* NOTE: must be last enum otherwise parsing will not work */ |
46 | } MSIError; | 46 | } MSIError; |
47 | 47 | ||
48 | /** | 48 | /** |
diff --git a/toxav/rtp.c b/toxav/rtp.c index e5f45310..77fce056 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c | |||
@@ -28,9 +28,9 @@ | |||
28 | 28 | ||
29 | #include "rtp.h" | 29 | #include "rtp.h" |
30 | #include <stdlib.h> | 30 | #include <stdlib.h> |
31 | void queue_message(RTPSession *_session, RTPMessage *_msg); | ||
32 | 31 | ||
33 | #define size_32 4 | 32 | #define size_32 4 |
33 | #define RTCP_REPORT_INTERVAL_MS 500 | ||
34 | 34 | ||
35 | #define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0) | 35 | #define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0) |
36 | #define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0) | 36 | #define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0) |
@@ -46,24 +46,236 @@ void queue_message(RTPSession *_session, RTPMessage *_msg); | |||
46 | #define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7) | 46 | #define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7) |
47 | #define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f) | 47 | #define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f) |
48 | 48 | ||
49 | /** | 49 | |
50 | * Checks if message came in late. | 50 | typedef struct { |
51 | */ | 51 | uint64_t timestamp; /* in ms */ |
52 | static int check_late_message (RTPSession *session, RTPMessage *msg) | 52 | |
53 | uint32_t packets_missing; | ||
54 | uint32_t expected_packets; | ||
55 | /* ... other stuff in the future */ | ||
56 | } RTCPReport; | ||
57 | |||
58 | typedef struct RTCPSession_s { | ||
59 | uint8_t prefix; | ||
60 | uint64_t last_sent_report_ts; | ||
61 | uint32_t last_missing_packets; | ||
62 | uint32_t last_expected_packets; | ||
63 | |||
64 | RingBuffer* pl_stats; /* Packet loss stats over time */ | ||
65 | } RTCPSession; | ||
66 | |||
67 | |||
68 | |||
69 | /* queue_message() is defined in codec.c */ | ||
70 | void queue_message(RTPSession *session, RTPMessage *msg); | ||
71 | RTPHeader *parse_header_in ( const uint8_t *payload, int length ); | ||
72 | RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length ); | ||
73 | RTPMessage *msg_parse ( const uint8_t *data, int length ); | ||
74 | uint8_t *parse_header_out ( const RTPHeader* header, uint8_t* payload ); | ||
75 | uint8_t *parse_ext_header_out ( const RTPExtHeader* header, uint8_t* payload ); | ||
76 | void build_header ( RTPSession* session, RTPHeader* header ); | ||
77 | void send_rtcp_report ( RTCPSession* session, Messenger* m, int32_t friendnumber ); | ||
78 | int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object ); | ||
79 | int handle_rtcp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object ); | ||
80 | |||
81 | |||
82 | |||
83 | |||
84 | RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) | ||
53 | { | 85 | { |
54 | /* | 86 | RTPSession *retu = calloc(1, sizeof(RTPSession)); |
55 | * Check Sequence number. If this new msg has lesser number then the session->rsequnum | 87 | |
56 | * it shows that the message came in late. Also check timestamp to be 100% certain. | 88 | if ( !retu ) { |
57 | * | 89 | LOGGER_WARNING("Alloc failed! Program might misbehave!"); |
58 | */ | 90 | return NULL; |
59 | return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1; | 91 | } |
92 | |||
93 | retu->version = RTP_VERSION; /* It's always 2 */ | ||
94 | retu->padding = 0; /* If some additional data is needed about the packet */ | ||
95 | retu->extension = 0; /* If extension to header is needed */ | ||
96 | retu->cc = 1; /* Amount of contributors */ | ||
97 | retu->csrc = NULL; /* Container */ | ||
98 | retu->ssrc = random_int(); | ||
99 | retu->marker = 0; | ||
100 | retu->payload_type = payload_type % 128; | ||
101 | |||
102 | retu->m = messenger; | ||
103 | retu->dest = friend_num; | ||
104 | retu->rsequnum = retu->sequnum = 0; | ||
105 | retu->ext_header = NULL; /* When needed allocate */ | ||
106 | |||
107 | if ( !(retu->csrc = calloc(1, sizeof(uint32_t))) ) { | ||
108 | LOGGER_WARNING("Alloc failed! Program might misbehave!"); | ||
109 | free(retu); | ||
110 | return NULL; | ||
111 | } | ||
112 | |||
113 | retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */ | ||
114 | |||
115 | /* Also set payload type as prefix */ | ||
116 | retu->prefix = payload_type; | ||
117 | |||
118 | |||
119 | /* Initialize rtcp session */ | ||
120 | if (!(retu->rtcp = calloc(1, sizeof(RTCPSession)))) { | ||
121 | LOGGER_WARNING("Alloc failed! Program might misbehave!"); | ||
122 | free(retu->csrc); | ||
123 | free(retu); | ||
124 | return NULL; | ||
125 | } | ||
126 | |||
127 | retu->rtcp->prefix = 222 + payload_type % 192; | ||
128 | retu->rtcp->pl_stats = rb_new(4); | ||
129 | |||
130 | return retu; | ||
60 | } | 131 | } |
132 | void rtp_kill ( RTPSession *session ) | ||
133 | { | ||
134 | if ( !session ) return; | ||
61 | 135 | ||
136 | rtp_stop_receiving (session); | ||
62 | 137 | ||
63 | /** | 138 | free ( session->ext_header ); |
64 | * Extracts header from payload. | 139 | free ( session->csrc ); |
65 | */ | 140 | |
66 | RTPHeader *extract_header ( const uint8_t *payload, int length ) | 141 | void* t; |
142 | while (!rb_empty(session->rtcp->pl_stats)) { | ||
143 | rb_read(session->rtcp->pl_stats, (void**) &t); | ||
144 | free(t); | ||
145 | } | ||
146 | rb_free(session->rtcp->pl_stats); | ||
147 | |||
148 | LOGGER_DEBUG("Terminated RTP session: %p", session); | ||
149 | |||
150 | /* And finally free session */ | ||
151 | free ( session ); | ||
152 | } | ||
153 | void rtp_do(RTPSession *session) | ||
154 | { | ||
155 | if (!session || !session->rtcp) | ||
156 | return; | ||
157 | |||
158 | if (current_time_monotonic() - session->rtcp->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) { | ||
159 | send_rtcp_report(session->rtcp, session->m, session->dest); | ||
160 | } | ||
161 | |||
162 | if (rb_full(session->rtcp->pl_stats)) { | ||
163 | RTCPReport* reports[4]; | ||
164 | |||
165 | int i = 0; | ||
166 | for (; rb_read(session->rtcp->pl_stats, (void**) reports + i); i++); | ||
167 | |||
168 | /* Check for timed out reports (> 6 sec) */ | ||
169 | uint64_t now = current_time_monotonic(); | ||
170 | for (i = 0; i < 4 && now - reports[i]->timestamp < 6000; i ++); | ||
171 | for (; i < 4; i ++) { | ||
172 | rb_write(session->rtcp->pl_stats, reports[i]); | ||
173 | reports[i] = NULL; | ||
174 | } | ||
175 | if (!rb_empty(session->rtcp->pl_stats)) { | ||
176 | for (i = 0; reports[i] != NULL; i ++) | ||
177 | free(reports[i]); | ||
178 | return; /* As some reports are timed out, we need more... */ | ||
179 | } | ||
180 | |||
181 | /* We have 4 on-time reports so we can proceed */ | ||
182 | uint32_t quality_loss = 0; | ||
183 | for (i = 0; i < 4; i++) { | ||
184 | uint32_t idx = reports[i]->packets_missing * 100 / reports[i]->expected_packets; | ||
185 | quality_loss += idx; | ||
186 | } | ||
187 | |||
188 | if (quality_loss > 40) { | ||
189 | LOGGER_DEBUG("Packet loss detected"); | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | int rtp_start_receiving(RTPSession* session) | ||
194 | { | ||
195 | if (session == NULL) | ||
196 | return -1; | ||
197 | |||
198 | if (custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, | ||
199 | handle_rtp_packet, session) == -1) { | ||
200 | LOGGER_WARNING("Failed to register rtp receive handler"); | ||
201 | return -1; | ||
202 | } | ||
203 | if (custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp->prefix, | ||
204 | handle_rtcp_packet, session->rtcp) == -1) { | ||
205 | LOGGER_WARNING("Failed to register rtcp receive handler"); | ||
206 | custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL); | ||
207 | return -1; | ||
208 | } | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | int rtp_stop_receiving(RTPSession* session) | ||
213 | { | ||
214 | if (session == NULL) | ||
215 | return -1; | ||
216 | |||
217 | custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL); | ||
218 | custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp->prefix, NULL, NULL); /* RTCP */ | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) | ||
223 | { | ||
224 | if ( !session ) { | ||
225 | LOGGER_WARNING("No session!"); | ||
226 | return -1; | ||
227 | } | ||
228 | |||
229 | uint8_t parsed[MAX_RTP_SIZE]; | ||
230 | uint8_t *it; | ||
231 | |||
232 | RTPHeader header[1]; | ||
233 | build_header(session, header); | ||
234 | |||
235 | uint32_t parsed_len = length + header->length + 1; | ||
236 | |||
237 | parsed[0] = session->prefix; | ||
238 | |||
239 | it = parse_header_out ( header, parsed + 1 ); | ||
240 | |||
241 | if ( session->ext_header ) { | ||
242 | parsed_len += ( 4 /* Minimum ext header len */ + session->ext_header->length * size_32 ); | ||
243 | it = parse_ext_header_out ( session->ext_header, it ); | ||
244 | } | ||
245 | |||
246 | memcpy ( it, data, length ); | ||
247 | |||
248 | if ( -1 == send_custom_lossy_packet(session->m, session->dest, parsed, parsed_len) ) { | ||
249 | LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); | ||
250 | return -1; | ||
251 | } | ||
252 | |||
253 | /* Set sequ number */ | ||
254 | session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1; | ||
255 | return 0; | ||
256 | } | ||
257 | void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) | ||
258 | { | ||
259 | if ( !session ) { | ||
260 | if ( msg->ext_header ) { | ||
261 | free ( msg->ext_header->table ); | ||
262 | free ( msg->ext_header ); | ||
263 | } | ||
264 | } else { | ||
265 | if ( msg->ext_header && session->ext_header != msg->ext_header ) { | ||
266 | free ( msg->ext_header->table ); | ||
267 | free ( msg->ext_header ); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | free ( msg->header ); | ||
272 | free ( msg ); | ||
273 | } | ||
274 | |||
275 | |||
276 | |||
277 | |||
278 | RTPHeader *parse_header_in ( const uint8_t *payload, int length ) | ||
67 | { | 279 | { |
68 | if ( !payload || !length ) { | 280 | if ( !payload || !length ) { |
69 | LOGGER_WARNING("No payload to extract!"); | 281 | LOGGER_WARNING("No payload to extract!"); |
@@ -111,8 +323,6 @@ RTPHeader *extract_header ( const uint8_t *payload, int length ) | |||
111 | return NULL; | 323 | return NULL; |
112 | } | 324 | } |
113 | 325 | ||
114 | memset(retu->csrc, 0, 16 * sizeof (uint32_t)); | ||
115 | |||
116 | retu->marker_payloadt = *it; | 326 | retu->marker_payloadt = *it; |
117 | ++it; | 327 | ++it; |
118 | retu->length = total; | 328 | retu->length = total; |
@@ -125,7 +335,6 @@ RTPHeader *extract_header ( const uint8_t *payload, int length ) | |||
125 | retu->ssrc = ntohl(retu->ssrc); | 335 | retu->ssrc = ntohl(retu->ssrc); |
126 | 336 | ||
127 | uint8_t x; | 337 | uint8_t x; |
128 | |||
129 | for ( x = 0; x < cc; x++ ) { | 338 | for ( x = 0; x < cc; x++ ) { |
130 | it += 4; | 339 | it += 4; |
131 | memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x])); | 340 | memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x])); |
@@ -134,11 +343,7 @@ RTPHeader *extract_header ( const uint8_t *payload, int length ) | |||
134 | 343 | ||
135 | return retu; | 344 | return retu; |
136 | } | 345 | } |
137 | 346 | RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length ) | |
138 | /** | ||
139 | * Extracts external header from payload. Must be called AFTER extract_header()! | ||
140 | */ | ||
141 | RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length ) | ||
142 | { | 347 | { |
143 | const uint8_t *it = payload; | 348 | const uint8_t *it = payload; |
144 | 349 | ||
@@ -182,11 +387,47 @@ RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length ) | |||
182 | 387 | ||
183 | return retu; | 388 | return retu; |
184 | } | 389 | } |
390 | RTPMessage *msg_parse ( const uint8_t *data, int length ) | ||
391 | { | ||
392 | RTPMessage *retu = calloc(1, sizeof (RTPMessage)); | ||
185 | 393 | ||
186 | /** | 394 | retu->header = parse_header_in ( data, length ); /* It allocates memory and all */ |
187 | * Adds header to payload. Make sure _payload_ has enough space. | 395 | |
188 | */ | 396 | if ( !retu->header ) { |
189 | uint8_t *add_header ( RTPHeader *header, uint8_t *payload ) | 397 | LOGGER_WARNING("Header failed to extract!"); |
398 | free(retu); | ||
399 | return NULL; | ||
400 | } | ||
401 | |||
402 | uint16_t from_pos = retu->header->length; | ||
403 | retu->length = length - from_pos; | ||
404 | |||
405 | if ( GET_FLAG_EXTENSION ( retu->header ) ) { | ||
406 | retu->ext_header = parse_ext_header_in ( data + from_pos, length ); | ||
407 | |||
408 | if ( retu->ext_header ) { | ||
409 | retu->length -= ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 ); | ||
410 | from_pos += ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 ); | ||
411 | } else { /* Error */ | ||
412 | LOGGER_WARNING("Ext Header failed to extract!"); | ||
413 | rtp_free_msg(NULL, retu); | ||
414 | return NULL; | ||
415 | } | ||
416 | } else { | ||
417 | retu->ext_header = NULL; | ||
418 | } | ||
419 | |||
420 | if ( length - from_pos <= MAX_RTP_SIZE ) | ||
421 | memcpy ( retu->data, data + from_pos, length - from_pos ); | ||
422 | else { | ||
423 | LOGGER_WARNING("Invalid length!"); | ||
424 | rtp_free_msg(NULL, retu); | ||
425 | return NULL; | ||
426 | } | ||
427 | |||
428 | return retu; | ||
429 | } | ||
430 | uint8_t *parse_header_out ( const RTPHeader *header, uint8_t *payload ) | ||
190 | { | 431 | { |
191 | uint8_t cc = GET_FLAG_CSRCC ( header ); | 432 | uint8_t cc = GET_FLAG_CSRCC ( header ); |
192 | uint8_t *it = payload; | 433 | uint8_t *it = payload; |
@@ -223,11 +464,7 @@ uint8_t *add_header ( RTPHeader *header, uint8_t *payload ) | |||
223 | 464 | ||
224 | return it + 4; | 465 | return it + 4; |
225 | } | 466 | } |
226 | 467 | uint8_t *parse_ext_header_out ( const RTPExtHeader *header, uint8_t *payload ) | |
227 | /** | ||
228 | * Adds extension header to payload. Make sure _payload_ has enough space. | ||
229 | */ | ||
230 | uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload ) | ||
231 | { | 468 | { |
232 | uint8_t *it = payload; | 469 | uint8_t *it = payload; |
233 | uint16_t length; | 470 | uint16_t length; |
@@ -242,9 +479,7 @@ uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload ) | |||
242 | it -= 2; /* Return to 0 position */ | 479 | it -= 2; /* Return to 0 position */ |
243 | 480 | ||
244 | if ( header->table ) { | 481 | if ( header->table ) { |
245 | |||
246 | uint16_t x; | 482 | uint16_t x; |
247 | |||
248 | for ( x = 0; x < header->length; x++ ) { | 483 | for ( x = 0; x < header->length; x++ ) { |
249 | it += 4; | 484 | it += 4; |
250 | entry = htonl(header->table[x]); | 485 | entry = htonl(header->table[x]); |
@@ -254,92 +489,45 @@ uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload ) | |||
254 | 489 | ||
255 | return it + 4; | 490 | return it + 4; |
256 | } | 491 | } |
257 | 492 | void build_header ( RTPSession *session, RTPHeader *header ) | |
258 | /** | ||
259 | * Builds header from control session values. | ||
260 | */ | ||
261 | RTPHeader *build_header ( RTPSession *session ) | ||
262 | { | 493 | { |
263 | RTPHeader *retu = calloc ( 1, sizeof (RTPHeader) ); | 494 | ADD_FLAG_VERSION ( header, session->version ); |
264 | 495 | ADD_FLAG_PADDING ( header, session->padding ); | |
265 | if ( !retu ) { | 496 | ADD_FLAG_EXTENSION ( header, session->extension ); |
266 | LOGGER_WARNING("Alloc failed! Program might misbehave!"); | 497 | ADD_FLAG_CSRCC ( header, session->cc ); |
267 | return NULL; | 498 | ADD_SETTING_MARKER ( header, session->marker ); |
268 | } | 499 | ADD_SETTING_PAYLOAD ( header, session->payload_type ); |
269 | |||
270 | ADD_FLAG_VERSION ( retu, session->version ); | ||
271 | ADD_FLAG_PADDING ( retu, session->padding ); | ||
272 | ADD_FLAG_EXTENSION ( retu, session->extension ); | ||
273 | ADD_FLAG_CSRCC ( retu, session->cc ); | ||
274 | ADD_SETTING_MARKER ( retu, session->marker ); | ||
275 | ADD_SETTING_PAYLOAD ( retu, session->payload_type ); | ||
276 | 500 | ||
277 | retu->sequnum = session->sequnum; | 501 | header->sequnum = session->sequnum; |
278 | retu->timestamp = current_time_monotonic(); /* milliseconds */ | 502 | header->timestamp = current_time_monotonic(); /* milliseconds */ |
279 | retu->ssrc = session->ssrc; | 503 | header->ssrc = session->ssrc; |
280 | 504 | ||
281 | int i; | 505 | int i; |
282 | |||
283 | for ( i = 0; i < session->cc; i++ ) | 506 | for ( i = 0; i < session->cc; i++ ) |
284 | retu->csrc[i] = session->csrc[i]; | 507 | header->csrc[i] = session->csrc[i]; |
285 | |||
286 | retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 ); | ||
287 | 508 | ||
288 | return retu; | 509 | header->length = 12 /* Minimum header len */ + ( session->cc * size_32 ); |
289 | } | 510 | } |
290 | 511 | void send_rtcp_report(RTCPSession* session, Messenger* m, int32_t friendnumber) | |
291 | |||
292 | /** | ||
293 | * Parses data into RTPMessage struct. Stores headers separately from the payload data | ||
294 | * and so the length variable is set accordingly. | ||
295 | */ | ||
296 | RTPMessage *msg_parse ( const uint8_t *data, int length ) | ||
297 | { | 512 | { |
298 | RTPMessage *retu = calloc(1, sizeof (RTPMessage)); | 513 | if (session->last_expected_packets == 0) |
299 | 514 | return; | |
300 | retu->header = extract_header ( data, length ); /* It allocates memory and all */ | 515 | |
301 | 516 | uint8_t parsed[9]; | |
302 | if ( !retu->header ) { | 517 | parsed[0] = session->prefix; |
303 | LOGGER_WARNING("Header failed to extract!"); | 518 | |
304 | free(retu); | 519 | uint32_t packets_missing = htonl(session->last_missing_packets); |
305 | return NULL; | 520 | uint32_t expected_packets = htonl(session->last_expected_packets); |
306 | } | 521 | |
307 | 522 | memcpy(parsed + 1, &packets_missing, 4); | |
308 | uint16_t from_pos = retu->header->length; | 523 | memcpy(parsed + 5, &expected_packets, 4); |
309 | retu->length = length - from_pos; | 524 | |
310 | 525 | if (-1 == send_custom_lossy_packet(m, friendnumber, parsed, sizeof(parsed))) | |
311 | 526 | LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", sizeof(parsed), strerror(errno)); | |
312 | 527 | else | |
313 | if ( GET_FLAG_EXTENSION ( retu->header ) ) { | 528 | session->last_sent_report_ts = current_time_monotonic(); |
314 | retu->ext_header = extract_ext_header ( data + from_pos, length ); | ||
315 | |||
316 | if ( retu->ext_header ) { | ||
317 | retu->length -= ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 ); | ||
318 | from_pos += ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 ); | ||
319 | } else { /* Error */ | ||
320 | LOGGER_WARNING("Ext Header failed to extract!"); | ||
321 | rtp_free_msg(NULL, retu); | ||
322 | return NULL; | ||
323 | } | ||
324 | } else { | ||
325 | retu->ext_header = NULL; | ||
326 | } | ||
327 | |||
328 | if ( length - from_pos <= MAX_RTP_SIZE ) | ||
329 | memcpy ( retu->data, data + from_pos, length - from_pos ); | ||
330 | else { | ||
331 | LOGGER_WARNING("Invalid length!"); | ||
332 | rtp_free_msg(NULL, retu); | ||
333 | return NULL; | ||
334 | } | ||
335 | |||
336 | return retu; | ||
337 | } | 529 | } |
338 | 530 | int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object ) | |
339 | /** | ||
340 | * Callback for networking core. | ||
341 | */ | ||
342 | int rtp_handle_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object ) | ||
343 | { | 531 | { |
344 | RTPSession *session = object; | 532 | RTPSession *session = object; |
345 | RTPMessage *msg; | 533 | RTPMessage *msg; |
@@ -357,178 +545,37 @@ int rtp_handle_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, | |||
357 | } | 545 | } |
358 | 546 | ||
359 | /* Check if message came in late */ | 547 | /* Check if message came in late */ |
360 | if ( check_late_message(session, msg) < 0 ) { /* Not late */ | 548 | if ( msg->header->sequnum > session->rsequnum && msg->header->timestamp > session->rtimestamp ) { |
549 | /* Not late */ | ||
361 | session->rsequnum = msg->header->sequnum; | 550 | session->rsequnum = msg->header->sequnum; |
362 | session->timestamp = msg->header->timestamp; | 551 | session->rtimestamp = msg->header->timestamp; |
363 | } | 552 | } |
364 | 553 | ||
365 | queue_message(session, msg); | 554 | queue_message(session, msg); |
366 | return 0; | 555 | return 0; |
367 | } | 556 | } |
368 | 557 | int handle_rtcp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object ) | |
369 | /** | ||
370 | * Allocate message and store data there | ||
371 | */ | ||
372 | RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length ) | ||
373 | { | 558 | { |
374 | if ( !session ) { | 559 | if (length < 9) |
375 | LOGGER_WARNING("No session!"); | 560 | return -1; |
376 | return NULL; | ||
377 | } | ||
378 | |||
379 | uint8_t *from_pos; | ||
380 | RTPMessage *retu = calloc(1, sizeof (RTPMessage)); | ||
381 | |||
382 | if ( !retu ) { | ||
383 | LOGGER_WARNING("Alloc failed! Program might misbehave!"); | ||
384 | return NULL; | ||
385 | } | ||
386 | |||
387 | /* Sets header values and copies the extension header in retu */ | ||
388 | retu->header = build_header ( session ); /* It allocates memory and all */ | ||
389 | retu->ext_header = session->ext_header; | ||
390 | |||
391 | |||
392 | uint32_t total_length = length + retu->header->length + 1; | ||
393 | |||
394 | retu->data[0] = session->prefix; | ||
395 | |||
396 | if ( retu->ext_header ) { | ||
397 | total_length += ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 ); | ||
398 | |||
399 | from_pos = add_header ( retu->header, retu->data + 1 ); | ||
400 | from_pos = add_ext_header ( retu->ext_header, from_pos + 1 ); | ||
401 | } else { | ||
402 | from_pos = add_header ( retu->header, retu->data + 1 ); | ||
403 | } | ||
404 | |||
405 | /* | ||
406 | * Parses the extension header into the message | ||
407 | * Of course if any | ||
408 | */ | ||
409 | |||
410 | /* Appends data on to retu->data */ | ||
411 | memcpy ( from_pos, data, length ); | ||
412 | |||
413 | retu->length = total_length; | ||
414 | |||
415 | return retu; | ||
416 | } | ||
417 | |||
418 | |||
419 | |||
420 | RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ) | ||
421 | { | ||
422 | RTPSession *retu = calloc(1, sizeof(RTPSession)); | ||
423 | |||
424 | if ( !retu ) { | ||
425 | LOGGER_WARNING("Alloc failed! Program might misbehave!"); | ||
426 | return NULL; | ||
427 | } | ||
428 | |||
429 | retu->version = RTP_VERSION; /* It's always 2 */ | ||
430 | retu->padding = 0; /* If some additional data is needed about the packet */ | ||
431 | retu->extension = 0; /* If extension to header is needed */ | ||
432 | retu->cc = 1; /* Amount of contributors */ | ||
433 | retu->csrc = NULL; /* Container */ | ||
434 | retu->ssrc = random_int(); | ||
435 | retu->marker = 0; | ||
436 | retu->payload_type = payload_type % 128; | ||
437 | |||
438 | retu->dest = friend_num; | ||
439 | |||
440 | retu->rsequnum = retu->sequnum = 0; | ||
441 | |||
442 | retu->ext_header = NULL; /* When needed allocate */ | ||
443 | |||
444 | |||
445 | if ( !(retu->csrc = calloc(1, sizeof (uint32_t))) ) { | ||
446 | LOGGER_WARNING("Alloc failed! Program might misbehave!"); | ||
447 | free(retu); | ||
448 | return NULL; | ||
449 | } | ||
450 | |||
451 | retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */ | ||
452 | |||
453 | /* Also set payload type as prefix */ | ||
454 | retu->prefix = payload_type; | ||
455 | |||
456 | retu->m = messenger; | ||
457 | /* | ||
458 | * | ||
459 | */ | ||
460 | return retu; | ||
461 | } | ||
462 | |||
463 | void rtp_kill ( RTPSession *session ) | ||
464 | { | ||
465 | if ( !session ) return; | ||
466 | |||
467 | rtp_stop_receiving (session); | ||
468 | |||
469 | free ( session->ext_header ); | ||
470 | free ( session->csrc ); | ||
471 | |||
472 | LOGGER_DEBUG("Terminated RTP session: %p", session); | ||
473 | |||
474 | /* And finally free session */ | ||
475 | free ( session ); | ||
476 | } | ||
477 | |||
478 | int rtp_start_receiving(RTPSession* session) | ||
479 | { | ||
480 | if (session == NULL) | ||
481 | return 0; | ||
482 | 561 | ||
483 | LOGGER_DEBUG("Registering packet handler: pt: %d; friend: %d", session->prefix, session->dest); | 562 | RTCPSession* session = object; |
484 | return custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, | 563 | RTCPReport* report = malloc(sizeof(RTCPReport)); |
485 | rtp_handle_packet, session); | ||
486 | } | ||
487 | |||
488 | int rtp_stop_receiving(RTPSession* session) | ||
489 | { | ||
490 | if (session == NULL) | ||
491 | return 0; | ||
492 | 564 | ||
493 | LOGGER_DEBUG("Unregistering packet handler: pt: %d; friend: %d", session->prefix, session->dest); | 565 | memcpy(&report->packets_missing, data + 1, 4); |
494 | return custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, | 566 | memcpy(&report->expected_packets, data + 5, 4); |
495 | NULL, NULL); | ||
496 | } | ||
497 | |||
498 | int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) | ||
499 | { | ||
500 | RTPMessage *msg = rtp_new_message (session, data, length); | ||
501 | 567 | ||
502 | if ( !msg ) return -1; | 568 | report->packets_missing = ntohl(report->packets_missing); |
569 | report->expected_packets = ntohl(report->expected_packets); | ||
503 | 570 | ||
504 | if ( -1 == send_custom_lossy_packet(session->m, session->dest, msg->data, msg->length) ) { | 571 | /* This would cause undefined behaviour */ |
505 | LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); | 572 | if (report->expected_packets == 0) { |
506 | rtp_free_msg ( session, msg ); | 573 | free(report); |
507 | return -1; | 574 | return 0; |
508 | } | 575 | } |
509 | 576 | ||
577 | report->timestamp = current_time_monotonic(); | ||
510 | 578 | ||
511 | /* Set sequ number */ | 579 | free(rb_write(session->pl_stats, report)); |
512 | session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1; | ||
513 | rtp_free_msg ( session, msg ); | ||
514 | |||
515 | return 0; | 580 | return 0; |
516 | } | 581 | } \ No newline at end of file |
517 | |||
518 | void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) | ||
519 | { | ||
520 | if ( !session ) { | ||
521 | if ( msg->ext_header ) { | ||
522 | free ( msg->ext_header->table ); | ||
523 | free ( msg->ext_header ); | ||
524 | } | ||
525 | } else { | ||
526 | if ( msg->ext_header && session->ext_header != msg->ext_header ) { | ||
527 | free ( msg->ext_header->table ); | ||
528 | free ( msg->ext_header ); | ||
529 | } | ||
530 | } | ||
531 | |||
532 | free ( msg->header ); | ||
533 | free ( msg ); | ||
534 | } | ||
diff --git a/toxav/rtp.h b/toxav/rtp.h index 6b796d5a..fa5af9fe 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h | |||
@@ -23,8 +23,6 @@ | |||
23 | #define RTP_H | 23 | #define RTP_H |
24 | 24 | ||
25 | #define RTP_VERSION 2 | 25 | #define RTP_VERSION 2 |
26 | #include <inttypes.h> | ||
27 | // #include <pthread.h> | ||
28 | 26 | ||
29 | #include "../toxcore/Messenger.h" | 27 | #include "../toxcore/Messenger.h" |
30 | 28 | ||
@@ -51,8 +49,8 @@ typedef enum { | |||
51 | rtp_TypeVideo | 49 | rtp_TypeVideo |
52 | } RTPPayloadType; | 50 | } RTPPayloadType; |
53 | 51 | ||
54 | /** | 52 | /** |
55 | * Standard rtp header | 53 | * Standard rtp header. |
56 | */ | 54 | */ |
57 | typedef struct { | 55 | typedef struct { |
58 | uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */ | 56 | uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */ |
@@ -62,17 +60,14 @@ typedef struct { | |||
62 | uint32_t ssrc; /* SSRC */ | 60 | uint32_t ssrc; /* SSRC */ |
63 | uint32_t csrc[16]; /* CSRC's table */ | 61 | uint32_t csrc[16]; /* CSRC's table */ |
64 | uint32_t length; /* Length of the header in payload string. */ | 62 | uint32_t length; /* Length of the header in payload string. */ |
65 | |||
66 | } RTPHeader; | 63 | } RTPHeader; |
67 | 64 | /** | |
68 | /** | ||
69 | * Standard rtp extension header. | 65 | * Standard rtp extension header. |
70 | */ | 66 | */ |
71 | typedef struct { | 67 | typedef struct { |
72 | uint16_t type; /* Extension profile */ | 68 | uint16_t type; /* Extension profile */ |
73 | uint16_t length; /* Number of extensions */ | 69 | uint16_t length; /* Number of extensions */ |
74 | uint32_t *table; /* Extension's table */ | 70 | uint32_t *table; /* Extension's table */ |
75 | |||
76 | } RTPExtHeader; | 71 | } RTPExtHeader; |
77 | 72 | ||
78 | /** | 73 | /** |
@@ -90,31 +85,32 @@ typedef struct { | |||
90 | * RTP control session. | 85 | * RTP control session. |
91 | */ | 86 | */ |
92 | typedef struct { | 87 | typedef struct { |
93 | uint8_t version; | 88 | uint8_t version; |
94 | uint8_t padding; | 89 | uint8_t padding; |
95 | uint8_t extension; | 90 | uint8_t extension; |
96 | uint8_t cc; | 91 | uint8_t cc; |
97 | uint8_t marker; | 92 | uint8_t marker; |
98 | uint8_t payload_type; | 93 | uint8_t payload_type; |
99 | uint16_t sequnum; /* Set when sending */ | 94 | uint16_t sequnum; /* Sending sequence number */ |
100 | uint16_t rsequnum; /* Check when recving msg */ | 95 | uint16_t rsequnum; /* Receiving sequence number */ |
101 | uint32_t timestamp; | 96 | uint32_t rtimestamp; |
102 | uint32_t ssrc; | 97 | uint32_t ssrc; |
103 | uint32_t *csrc; | 98 | uint32_t *csrc; |
104 | 99 | ||
105 | /* If some additional data must be sent via message | 100 | /* If some additional data must be sent via message |
106 | * apply it here. Only by allocating this member you will be | 101 | * apply it here. Only by allocating this member you will be |
107 | * automatically placing it within a message. | 102 | * automatically placing it within a message. |
108 | */ | 103 | */ |
109 | RTPExtHeader *ext_header; | 104 | RTPExtHeader *ext_header; |
110 | 105 | ||
111 | /* Msg prefix for core to know when recving */ | 106 | /* Msg prefix for core to know when recving */ |
112 | uint8_t prefix; | 107 | uint8_t prefix; |
113 | 108 | ||
114 | int dest; | 109 | int dest; |
115 | 110 | ||
116 | struct CSession_s *cs; | 111 | struct RTCPSession_s *rtcp; |
117 | Messenger *m; | 112 | struct CSession_s *cs; |
113 | Messenger *m; | ||
118 | 114 | ||
119 | } RTPSession; | 115 | } RTPSession; |
120 | 116 | ||
@@ -129,6 +125,11 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num ); | |||
129 | void rtp_kill ( RTPSession* session ); | 125 | void rtp_kill ( RTPSession* session ); |
130 | 126 | ||
131 | /** | 127 | /** |
128 | * Do periodical rtp work. | ||
129 | */ | ||
130 | void rtp_do(RTPSession *session); | ||
131 | |||
132 | /** | ||
132 | * By default rtp is not in receiving state | 133 | * By default rtp is not in receiving state |
133 | */ | 134 | */ |
134 | int rtp_start_receiving (RTPSession *session); | 135 | int rtp_start_receiving (RTPSession *session); |
diff --git a/toxav/toxav.c b/toxav/toxav.c index bd788d7d..721b9d91 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c | |||
@@ -220,6 +220,9 @@ void toxav_iterate(ToxAV* av) | |||
220 | LOGGED_UNLOCK(av->mutex); | 220 | LOGGED_UNLOCK(av->mutex); |
221 | 221 | ||
222 | cs_do(i->cs); | 222 | cs_do(i->cs); |
223 | rtp_do(i->rtps[0]); | ||
224 | rtp_do(i->rtps[1]); | ||
225 | |||
223 | if (i->last_self_capabilities & msi_CapRAudio) /* Receiving audio */ | 226 | if (i->last_self_capabilities & msi_CapRAudio) /* Receiving audio */ |
224 | rc = MIN(i->cs->last_packet_frame_duration, rc); | 227 | rc = MIN(i->cs->last_packet_frame_duration, rc); |
225 | if (i->last_self_capabilities & msi_CapRVideo) /* Receiving video */ | 228 | if (i->last_self_capabilities & msi_CapRVideo) /* Receiving video */ |