summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormannol <eniz_vukovic@hotmail.com>2015-04-13 01:45:53 +0200
committermannol <eniz_vukovic@hotmail.com>2015-04-13 01:45:53 +0200
commit2465f486acd90ed8395c8a83a13af09ecd024c98 (patch)
tree4abe53d39eb07a45e5ed4d8894b7ae038e2bb705
parentb2d88a4544a81a217db18b60d91a44d85821db3d (diff)
Started custom RTCP
-rw-r--r--toxav/av_test.c107
-rw-r--r--toxav/codec.c98
-rw-r--r--toxav/codec.h8
-rw-r--r--toxav/msi.h4
-rw-r--r--toxav/rtp.c589
-rw-r--r--toxav/rtp.h49
-rw-r--r--toxav/toxav.c3
-rw-r--r--toxcore/util.c73
-rw-r--r--toxcore/util.h10
9 files changed, 497 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
66const char* vdout = "AV Test"; 91const char* vdout = "AV Test"; /* Video output */
67PaStream* adout = NULL; 92PaStream* adout = NULL; /* Audio output */
68 93
69const char* stringify_state(TOXAV_CALL_STATE s) 94const 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
307int main (int argc, char** argv) 332int 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
54typedef ARRAY(uint8_t) Payload; 54typedef ARRAY(uint8_t) Payload;
55 55
56typedef struct {
57 uint16_t size; /* Max size */
58 uint16_t start;
59 uint16_t end;
60 Payload **packets;
61} PayloadBuffer;
62
63static bool buffer_full(const PayloadBuffer *b)
64{
65 return (b->end + 1) % b->size == b->start;
66}
67
68static bool buffer_empty(const PayloadBuffer *b)
69{
70 return b->end == b->start;
71}
72
73static 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
81static void buffer_read(PayloadBuffer *b, Payload **p)
82{
83 *p = b->packets[b->start];
84 b->start = (b->start + 1) % b->size;
85}
86
87static 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
96static 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
112static 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 */
122typedef struct JitterBuffer_s { 57typedef 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
340void cs_do(CSession *cs) 274void 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
451CSession *cs_new(uint32_t peer_video_frame_piece_size) 384CSession *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
544VIDEO_DECODER_CLEANUP: 477VIDEO_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);
548AUDIO_DECODER_CLEANUP: 481AUDIO_DECODER_CLEANUP:
@@ -553,7 +486,6 @@ FAILURE:
553 free(cs); 486 free(cs);
554 return NULL; 487 return NULL;
555} 488}
556
557void cs_kill(CSession *cs) 489void 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
580void cs_init_video_splitter_cycle(CSession* cs) 512void 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
586int cs_update_video_splitter_cycle(CSession *cs, const uint8_t *payload, uint16_t length) 517int 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
594const uint8_t *cs_iterate_split_video_frame(CSession *cs, uint16_t *size) 524const 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
622int cs_reconfigure_video_encoder(CSession* cs, int32_t bitrate, uint16_t width, uint16_t height) 549int 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
641int cs_reconfigure_audio_encoder(CSession* cs, int32_t bitrate, int32_t sampling_rate, uint8_t channels) 567int 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 */
673void queue_message(RTPSession *session, RTPMessage *msg) 597void 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
126int cs_reconfigure_video_encoder(CSession* cs, int32_t bitrate, uint16_t width, uint16_t height); 126int cs_reconfigure_video_encoder(CSession* cs, int32_t bitrate, uint16_t width, uint16_t height);
127int cs_reconfigure_audio_encoder(CSession* cs, int32_t bitrate, int32_t sampling_rate, uint8_t channels); 127int 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 */
131void 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>
31void 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. 50typedef struct {
51 */ 51 uint64_t timestamp; /* in ms */
52static 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
58typedef 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 */
70void queue_message(RTPSession *session, RTPMessage *msg);
71RTPHeader *parse_header_in ( const uint8_t *payload, int length );
72RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length );
73RTPMessage *msg_parse ( const uint8_t *data, int length );
74uint8_t *parse_header_out ( const RTPHeader* header, uint8_t* payload );
75uint8_t *parse_ext_header_out ( const RTPExtHeader* header, uint8_t* payload );
76void build_header ( RTPSession* session, RTPHeader* header );
77void send_rtcp_report ( RTCPSession* session, Messenger* m, int32_t friendnumber );
78int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object );
79int handle_rtcp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object );
80
81
82
83
84RTPSession *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}
132void 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
66RTPHeader *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}
153void 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}
193int 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}
212int 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}
222int 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}
257void 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
278RTPHeader *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 346RTPExtHeader *parse_ext_header_in ( const uint8_t *payload, uint16_t length )
138/**
139 * Extracts external header from payload. Must be called AFTER extract_header()!
140 */
141RTPExtHeader *extract_ext_header ( const uint8_t *payload, uint16_t length )
142{ 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}
390RTPMessage *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 ) {
189uint8_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}
430uint8_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 467uint8_t *parse_ext_header_out ( const RTPExtHeader *header, uint8_t *payload )
227/**
228 * Adds extension header to payload. Make sure _payload_ has enough space.
229 */
230uint8_t *add_ext_header ( RTPExtHeader *header, uint8_t *payload )
231{ 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 492void build_header ( RTPSession *session, RTPHeader *header )
258/**
259 * Builds header from control session values.
260 */
261RTPHeader *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 511void 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 */
296RTPMessage *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 530int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object )
339/**
340 * Callback for networking core.
341 */
342int 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 557int 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 */
372RTPMessage *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
420RTPSession *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
463void 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
478int 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
488int 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
498int 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
518void 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 */
57typedef struct { 55typedef 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 */
71typedef struct { 67typedef 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 */
92typedef struct { 87typedef 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 );
129void rtp_kill ( RTPSession* session ); 125void rtp_kill ( RTPSession* session );
130 126
131/** 127/**
128 * Do periodical rtp work.
129 */
130void 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 */
134int rtp_start_receiving (RTPSession *session); 135int 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 */
diff --git a/toxcore/util.c b/toxcore/util.c
index 5a72c4a4..d6db946d 100644
--- a/toxcore/util.c
+++ b/toxcore/util.c
@@ -185,3 +185,76 @@ int create_recursive_mutex(pthread_mutex_t *mutex)
185 185
186 return 0; 186 return 0;
187} 187}
188
189
190struct RingBuffer {
191 uint16_t size; /* Max size */
192 uint16_t start;
193 uint16_t end;
194 void **data;
195};
196
197bool rb_full(const RingBuffer *b)
198{
199 return (b->end + 1) % b->size == b->start;
200}
201bool rb_empty(const RingBuffer *b)
202{
203 return b->end == b->start;
204}
205void* rb_write(RingBuffer *b, void *p)
206{
207 void* rc = NULL;
208 if ((b->end + 1) % b->size == b->start) /* full */
209 rc = b->data[b->start];
210
211 b->data[b->end] = p;
212 b->end = (b->end + 1) % b->size;
213
214 if (b->end == b->start)
215 b->start = (b->start + 1) % b->size;
216
217 return rc;
218}
219bool rb_read(RingBuffer *b, void **p)
220{
221 if (b->end == b->start) { /* Empty */
222 *p = NULL;
223 return false;
224 }
225
226 *p = b->data[b->start];
227 b->start = (b->start + 1) % b->size;
228 return true;
229}
230void rb_clear(RingBuffer *b)
231{
232 while (!rb_empty(b)) {
233 void *p;
234 rb_read(b, &p);
235 free(p);
236 }
237}
238RingBuffer *rb_new(int size)
239{
240 RingBuffer *buf = calloc(sizeof(RingBuffer), 1);
241
242 if (!buf) return NULL;
243
244 buf->size = size + 1; /* include empty elem */
245
246 if (!(buf->data = calloc(buf->size, sizeof(void *)))) {
247 free(buf);
248 return NULL;
249 }
250
251 return buf;
252}
253void rb_free(RingBuffer *b)
254{
255 if (b) {
256 rb_clear(b);
257 free(b->data);
258 free(b);
259 }
260} \ No newline at end of file
diff --git a/toxcore/util.h b/toxcore/util.h
index fab26e29..6c3d3b38 100644
--- a/toxcore/util.h
+++ b/toxcore/util.h
@@ -30,6 +30,7 @@
30#include <pthread.h> 30#include <pthread.h>
31 31
32#define MIN(a,b) (((a)<(b))?(a):(b)) 32#define MIN(a,b) (((a)<(b))?(a):(b))
33#define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; }
33 34
34void unix_time_update(); 35void unix_time_update();
35uint64_t unix_time(); 36uint64_t unix_time();
@@ -56,4 +57,13 @@ int load_state(load_state_callback_func load_state_callback, void *outer,
56/* Returns -1 if failed or 0 if success */ 57/* Returns -1 if failed or 0 if success */
57int create_recursive_mutex(pthread_mutex_t *mutex); 58int create_recursive_mutex(pthread_mutex_t *mutex);
58 59
60/* Ring buffer */
61typedef struct RingBuffer RingBuffer;
62bool rb_full(const RingBuffer *b);
63bool rb_empty(const RingBuffer *b);
64void* rb_write(RingBuffer* b, void* p);
65bool rb_read(RingBuffer* b, void** p);
66void rb_clear(RingBuffer *b);
67RingBuffer *rb_new(int size);
68void rb_free(RingBuffer *b);
59#endif /* __UTIL_H__ */ 69#endif /* __UTIL_H__ */