summaryrefslogtreecommitdiff
path: root/toxav
diff options
context:
space:
mode:
Diffstat (limited to 'toxav')
-rw-r--r--toxav/av_test.c59
-rw-r--r--toxav/codec.c5
-rw-r--r--toxav/rtp.c101
-rw-r--r--toxav/rtp.h16
-rw-r--r--toxav/toxav.c165
-rw-r--r--toxav/toxav.h26
6 files changed, 232 insertions, 140 deletions
diff --git a/toxav/av_test.c b/toxav/av_test.c
index c20d459b..6ebe0421 100644
--- a/toxav/av_test.c
+++ b/toxav/av_test.c
@@ -27,6 +27,9 @@
27#include "../toxcore/tox.h" 27#include "../toxcore/tox.h"
28#include "../toxcore/util.h" 28#include "../toxcore/util.h"
29 29
30#define LOGGING
31#include "../toxcore/logger.h"
32
30/* Playing audio data */ 33/* Playing audio data */
31#include <portaudio.h> 34#include <portaudio.h>
32/* Reading audio */ 35/* Reading audio */
@@ -80,6 +83,7 @@
80typedef struct { 83typedef struct {
81 bool incoming; 84 bool incoming;
82 uint32_t state; 85 uint32_t state;
86 uint32_t abitrate;
83} CallControl; 87} CallControl;
84 88
85struct toxav_thread_data { 89struct toxav_thread_data {
@@ -91,22 +95,6 @@ struct toxav_thread_data {
91const char* vdout = "AV Test"; /* Video output */ 95const char* vdout = "AV Test"; /* Video output */
92PaStream* adout = NULL; /* Audio output */ 96PaStream* adout = NULL; /* Audio output */
93 97
94const char* stringify_state(TOXAV_CALL_STATE s)
95{
96 static const char* strings[] =
97 {
98 "NOT SENDING",
99 "SENDING AUDIO",
100 "SENDING VIDEO",
101 "SENDING AUDIO AND VIDEO",
102 "PAUSED",
103 "END",
104 "ERROR"
105 };
106
107 return strings [s];
108}
109
110/** 98/**
111 * Callbacks 99 * Callbacks
112 */ 100 */
@@ -117,9 +105,20 @@ void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool
117} 105}
118void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) 106void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
119{ 107{
120 printf("Handling CALL STATE callback: %d\n", state);
121
122 ((CallControl*)user_data)->state = state; 108 ((CallControl*)user_data)->state = state;
109
110 if (state & TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE) {
111 uint32_t bitrate = ((CallControl*)user_data)->abitrate;
112
113 if (bitrate < 64) {
114 printf("Changing bitrate to: %d\n", 64);
115 toxav_set_audio_bit_rate(av, friend_number, 64, 0);
116 }
117 } else if (state & TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE) {
118
119 } else {
120 printf("Handling CALL STATE callback: %d\n", state);
121 }
123} 122}
124void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, 123void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
125 uint16_t width, uint16_t height, 124 uint16_t width, uint16_t height,
@@ -331,24 +330,6 @@ int print_help (const char* name)
331 330
332int main (int argc, char** argv) 331int main (int argc, char** argv)
333{ 332{
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;
352 Pa_Initialize(); 333 Pa_Initialize();
353 334
354 struct stat st; 335 struct stat st;
@@ -688,9 +669,11 @@ int main (int argc, char** argv)
688 memset(&AliceCC, 0, sizeof(CallControl)); 669 memset(&AliceCC, 0, sizeof(CallControl));
689 memset(&BobCC, 0, sizeof(CallControl)); 670 memset(&BobCC, 0, sizeof(CallControl));
690 671
672 AliceCC.abitrate = BobCC.abitrate = 8;
673
691 { /* Call */ 674 { /* Call */
692 TOXAV_ERR_CALL rc; 675 TOXAV_ERR_CALL rc;
693 toxav_call(AliceAV, 0, 48, 0, &rc); 676 toxav_call(AliceAV, 0, AliceCC.abitrate, 0, &rc);
694 677
695 if (rc != TOXAV_ERR_CALL_OK) { 678 if (rc != TOXAV_ERR_CALL_OK) {
696 printf("toxav_call failed: %d\n", rc); 679 printf("toxav_call failed: %d\n", rc);
@@ -703,7 +686,7 @@ int main (int argc, char** argv)
703 686
704 { /* Answer */ 687 { /* Answer */
705 TOXAV_ERR_ANSWER rc; 688 TOXAV_ERR_ANSWER rc;
706 toxav_answer(BobAV, 0, 48, 0, &rc); 689 toxav_answer(BobAV, 0, BobCC.abitrate, 0, &rc);
707 690
708 if (rc != TOXAV_ERR_ANSWER_OK) { 691 if (rc != TOXAV_ERR_ANSWER_OK) {
709 printf("toxav_answer failed: %d\n", rc); 692 printf("toxav_answer failed: %d\n", rc);
diff --git a/toxav/codec.c b/toxav/codec.c
index 671be1ac..1e8754e9 100644
--- a/toxav/codec.c
+++ b/toxav/codec.c
@@ -293,7 +293,7 @@ void cs_do(CSession *cs)
293 /* The maximum for 120 ms 48 KHz audio */ 293 /* The maximum for 120 ms 48 KHz audio */
294 int16_t tmp[5760]; 294 int16_t tmp[5760];
295 295
296 while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) { 296 if ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) {
297 LOGGED_UNLOCK(cs->queue_mutex); 297 LOGGED_UNLOCK(cs->queue_mutex);
298 298
299 if (success == 2) { 299 if (success == 2) {
@@ -327,7 +327,7 @@ void cs_do(CSession *cs)
327 if (!reconfigure_audio_decoder(cs, cs->last_packet_sampling_rate, cs->last_packet_channel_count)) { 327 if (!reconfigure_audio_decoder(cs, cs->last_packet_sampling_rate, cs->last_packet_channel_count)) {
328 LOGGER_WARNING("Failed to reconfigure decoder!"); 328 LOGGER_WARNING("Failed to reconfigure decoder!");
329 rtp_free_msg(NULL, msg); 329 rtp_free_msg(NULL, msg);
330 continue; 330 goto DONE;
331 } 331 }
332 332
333 rc = opus_decode(cs->audio_decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0); 333 rc = opus_decode(cs->audio_decoder, msg->data + 4, msg->length - 4, tmp, 5760, 0);
@@ -347,6 +347,7 @@ void cs_do(CSession *cs)
347 347
348 LOGGED_LOCK(cs->queue_mutex); 348 LOGGED_LOCK(cs->queue_mutex);
349 } 349 }
350 DONE:;
350 } 351 }
351 352
352 /********************* VIDEO *********************/ 353 /********************* VIDEO *********************/
diff --git a/toxav/rtp.c b/toxav/rtp.c
index 77fce056..665ebf86 100644
--- a/toxav/rtp.c
+++ b/toxav/rtp.c
@@ -50,15 +50,17 @@
50typedef struct { 50typedef struct {
51 uint64_t timestamp; /* in ms */ 51 uint64_t timestamp; /* in ms */
52 52
53 uint32_t packets_missing; 53 uint32_t received_packets;
54 uint32_t expected_packets; 54 uint32_t expected_packets;
55 /* ... other stuff in the future */ 55 /* ... other stuff in the future */
56} RTCPReport; 56} RTCPReport;
57 57
58typedef struct RTCPSession_s { 58typedef struct RTCPSession_s {
59 RTPSession *rtp_session;
60
59 uint8_t prefix; 61 uint8_t prefix;
60 uint64_t last_sent_report_ts; 62 uint64_t last_sent_report_ts;
61 uint32_t last_missing_packets; 63 uint32_t last_received_packets;
62 uint32_t last_expected_packets; 64 uint32_t last_expected_packets;
63 65
64 RingBuffer* pl_stats; /* Packet loss stats over time */ 66 RingBuffer* pl_stats; /* Packet loss stats over time */
@@ -66,6 +68,7 @@ typedef struct RTCPSession_s {
66 68
67 69
68 70
71
69/* queue_message() is defined in codec.c */ 72/* queue_message() is defined in codec.c */
70void queue_message(RTPSession *session, RTPMessage *msg); 73void queue_message(RTPSession *session, RTPMessage *msg);
71RTPHeader *parse_header_in ( const uint8_t *payload, int length ); 74RTPHeader *parse_header_in ( const uint8_t *payload, int length );
@@ -91,18 +94,12 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
91 } 94 }
92 95
93 retu->version = RTP_VERSION; /* It's always 2 */ 96 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(); 97 retu->ssrc = random_int();
99 retu->marker = 0;
100 retu->payload_type = payload_type % 128; 98 retu->payload_type = payload_type % 128;
101 99
100 retu->tstate = rtp_StateNormal;
102 retu->m = messenger; 101 retu->m = messenger;
103 retu->dest = friend_num; 102 retu->dest = friend_num;
104 retu->rsequnum = retu->sequnum = 0;
105 retu->ext_header = NULL; /* When needed allocate */
106 103
107 if ( !(retu->csrc = calloc(1, sizeof(uint32_t))) ) { 104 if ( !(retu->csrc = calloc(1, sizeof(uint32_t))) ) {
108 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 105 LOGGER_WARNING("Alloc failed! Program might misbehave!");
@@ -117,15 +114,16 @@ RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
117 114
118 115
119 /* Initialize rtcp session */ 116 /* Initialize rtcp session */
120 if (!(retu->rtcp = calloc(1, sizeof(RTCPSession)))) { 117 if (!(retu->rtcp_session = calloc(1, sizeof(RTCPSession)))) {
121 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 118 LOGGER_WARNING("Alloc failed! Program might misbehave!");
122 free(retu->csrc); 119 free(retu->csrc);
123 free(retu); 120 free(retu);
124 return NULL; 121 return NULL;
125 } 122 }
126 123
127 retu->rtcp->prefix = 222 + payload_type % 192; 124 retu->rtcp_session->prefix = 222 + payload_type % 192;
128 retu->rtcp->pl_stats = rb_new(4); 125 retu->rtcp_session->pl_stats = rb_new(4);
126 retu->rtcp_session->rtp_session = retu;
129 127
130 return retu; 128 return retu;
131} 129}
@@ -139,11 +137,11 @@ void rtp_kill ( RTPSession *session )
139 free ( session->csrc ); 137 free ( session->csrc );
140 138
141 void* t; 139 void* t;
142 while (!rb_empty(session->rtcp->pl_stats)) { 140 while (!rb_empty(session->rtcp_session->pl_stats)) {
143 rb_read(session->rtcp->pl_stats, (void**) &t); 141 rb_read(session->rtcp_session->pl_stats, (void**) &t);
144 free(t); 142 free(t);
145 } 143 }
146 rb_free(session->rtcp->pl_stats); 144 rb_free(session->rtcp_session->pl_stats);
147 145
148 LOGGER_DEBUG("Terminated RTP session: %p", session); 146 LOGGER_DEBUG("Terminated RTP session: %p", session);
149 147
@@ -152,41 +150,49 @@ void rtp_kill ( RTPSession *session )
152} 150}
153void rtp_do(RTPSession *session) 151void rtp_do(RTPSession *session)
154{ 152{
155 if (!session || !session->rtcp) 153 if (!session || !session->rtcp_session)
156 return; 154 return;
157 155
158 if (current_time_monotonic() - session->rtcp->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) { 156 if (current_time_monotonic() - session->rtcp_session->last_sent_report_ts >= RTCP_REPORT_INTERVAL_MS) {
159 send_rtcp_report(session->rtcp, session->m, session->dest); 157 send_rtcp_report(session->rtcp_session, session->m, session->dest);
160 } 158 }
161 159
162 if (rb_full(session->rtcp->pl_stats)) { 160 if (rb_full(session->rtcp_session->pl_stats)) {
163 RTCPReport* reports[4]; 161 RTCPReport* reports[4];
164 162
165 int i = 0; 163 int i = 0;
166 for (; rb_read(session->rtcp->pl_stats, (void**) reports + i); i++); 164 for (; rb_read(session->rtcp_session->pl_stats, (void**) reports + i); i++);
167 165
168 /* Check for timed out reports (> 6 sec) */ 166 /* Check for timed out reports (> 6 sec) */
169 uint64_t now = current_time_monotonic(); 167 uint64_t now = current_time_monotonic();
170 for (i = 0; i < 4 && now - reports[i]->timestamp < 6000; i ++); 168 for (i = 0; i < 4 && now - reports[i]->timestamp < 6000; i ++);
171 for (; i < 4; i ++) { 169 for (; i < 4; i ++) {
172 rb_write(session->rtcp->pl_stats, reports[i]); 170 rb_write(session->rtcp_session->pl_stats, reports[i]);
173 reports[i] = NULL; 171 reports[i] = NULL;
174 } 172 }
175 if (!rb_empty(session->rtcp->pl_stats)) { 173 if (!rb_empty(session->rtcp_session->pl_stats)) {
176 for (i = 0; reports[i] != NULL; i ++) 174 for (i = 0; reports[i] != NULL; i ++)
177 free(reports[i]); 175 free(reports[i]);
178 return; /* As some reports are timed out, we need more... */ 176 return; /* As some reports are timed out, we need more... */
179 } 177 }
180 178
181 /* We have 4 on-time reports so we can proceed */ 179 /* We have 4 on-time reports so we can proceed */
182 uint32_t quality_loss = 0; 180 uint32_t quality = 100;
183 for (i = 0; i < 4; i++) { 181 for (i = 0; i < 4; i++) {
184 uint32_t idx = reports[i]->packets_missing * 100 / reports[i]->expected_packets; 182 uint32_t idx = reports[i]->received_packets * 100 / reports[i]->expected_packets;
185 quality_loss += idx; 183 quality = MIN(quality, idx);
184 free(reports[i]);
186 } 185 }
187 186
188 if (quality_loss > 40) { 187 if (quality <= 70) {
189 LOGGER_DEBUG("Packet loss detected"); 188 session->tstate = rtp_StateBad;
189 LOGGER_WARNING("Stream quality: BAD");
190 } else if (quality >= 99) {
191 session->tstate = rtp_StateGood;
192 LOGGER_DEBUG("Stream quality: GOOD");
193 } else {
194 session->tstate = rtp_StateNormal;
195 LOGGER_DEBUG("Stream quality: NORMAL");
190 } 196 }
191 } 197 }
192} 198}
@@ -200,8 +206,8 @@ int rtp_start_receiving(RTPSession* session)
200 LOGGER_WARNING("Failed to register rtp receive handler"); 206 LOGGER_WARNING("Failed to register rtp receive handler");
201 return -1; 207 return -1;
202 } 208 }
203 if (custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp->prefix, 209 if (custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp_session->prefix,
204 handle_rtcp_packet, session->rtcp) == -1) { 210 handle_rtcp_packet, session->rtcp_session) == -1) {
205 LOGGER_WARNING("Failed to register rtcp receive handler"); 211 LOGGER_WARNING("Failed to register rtcp receive handler");
206 custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL); 212 custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL);
207 return -1; 213 return -1;
@@ -215,7 +221,7 @@ int rtp_stop_receiving(RTPSession* session)
215 return -1; 221 return -1;
216 222
217 custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, NULL, NULL); 223 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 */ 224 custom_lossy_packet_registerhandler(session->m, session->dest, session->rtcp_session->prefix, NULL, NULL); /* RTCP */
219 225
220 return 0; 226 return 0;
221} 227}
@@ -516,16 +522,21 @@ void send_rtcp_report(RTCPSession* session, Messenger* m, int32_t friendnumber)
516 uint8_t parsed[9]; 522 uint8_t parsed[9];
517 parsed[0] = session->prefix; 523 parsed[0] = session->prefix;
518 524
519 uint32_t packets_missing = htonl(session->last_missing_packets); 525 uint32_t received_packets = htonl(session->last_received_packets);
520 uint32_t expected_packets = htonl(session->last_expected_packets); 526 uint32_t expected_packets = htonl(session->last_expected_packets);
521 527
522 memcpy(parsed + 1, &packets_missing, 4); 528 memcpy(parsed + 1, &received_packets, 4);
523 memcpy(parsed + 5, &expected_packets, 4); 529 memcpy(parsed + 5, &expected_packets, 4);
524 530
525 if (-1 == send_custom_lossy_packet(m, friendnumber, parsed, sizeof(parsed))) 531 if (-1 == send_custom_lossy_packet(m, friendnumber, parsed, sizeof(parsed)))
526 LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", sizeof(parsed), strerror(errno)); 532 LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", sizeof(parsed), strerror(errno));
527 else 533 else {
534 LOGGER_DEBUG("Sent rtcp report: ex: %d rc: %d", session->last_expected_packets, session->last_received_packets);
535
536 session->last_received_packets = 0;
537 session->last_expected_packets = 0;
528 session->last_sent_report_ts = current_time_monotonic(); 538 session->last_sent_report_ts = current_time_monotonic();
539 }
529} 540}
530int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object ) 541int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object )
531{ 542{
@@ -545,12 +556,21 @@ int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data,
545 } 556 }
546 557
547 /* Check if message came in late */ 558 /* Check if message came in late */
548 if ( msg->header->sequnum > session->rsequnum && msg->header->timestamp > session->rtimestamp ) { 559 if ( msg->header->sequnum > session->rsequnum || msg->header->timestamp > session->rtimestamp ) {
549 /* Not late */ 560 /* Not late */
561 if (msg->header->sequnum > session->rsequnum)
562 session->rtcp_session->last_expected_packets += msg->header->sequnum - session->rsequnum;
563 else if (msg->header->sequnum < session->rsequnum)
564 session->rtcp_session->last_expected_packets += (msg->header->sequnum + 65535) - session->rsequnum;
565 else /* Usual case when transmission starts */
566 session->rtcp_session->last_expected_packets ++;
567
550 session->rsequnum = msg->header->sequnum; 568 session->rsequnum = msg->header->sequnum;
551 session->rtimestamp = msg->header->timestamp; 569 session->rtimestamp = msg->header->timestamp;
552 } 570 }
553 571
572 session->rtcp_session->last_received_packets ++;
573
554 queue_message(session, msg); 574 queue_message(session, msg);
555 return 0; 575 return 0;
556} 576}
@@ -562,14 +582,15 @@ int handle_rtcp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data
562 RTCPSession* session = object; 582 RTCPSession* session = object;
563 RTCPReport* report = malloc(sizeof(RTCPReport)); 583 RTCPReport* report = malloc(sizeof(RTCPReport));
564 584
565 memcpy(&report->packets_missing, data + 1, 4); 585 memcpy(&report->received_packets, data + 1, 4);
566 memcpy(&report->expected_packets, data + 5, 4); 586 memcpy(&report->expected_packets, data + 5, 4);
567 587
568 report->packets_missing = ntohl(report->packets_missing); 588 report->received_packets = ntohl(report->received_packets);
569 report->expected_packets = ntohl(report->expected_packets); 589 report->expected_packets = ntohl(report->expected_packets);
570 590
571 /* This would cause undefined behaviour */ 591 /* Invalid values */
572 if (report->expected_packets == 0) { 592 if (report->expected_packets == 0 || report->received_packets > report->expected_packets) {
593 LOGGER_WARNING("Malformed rtcp report! %d %d", report->expected_packets, report->received_packets);
573 free(report); 594 free(report);
574 return 0; 595 return 0;
575 } 596 }
@@ -577,5 +598,7 @@ int handle_rtcp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data
577 report->timestamp = current_time_monotonic(); 598 report->timestamp = current_time_monotonic();
578 599
579 free(rb_write(session->pl_stats, report)); 600 free(rb_write(session->pl_stats, report));
601
602 LOGGER_DEBUG("Got rtcp report: ex: %d rc: %d", report->expected_packets, report->received_packets);
580 return 0; 603 return 0;
581} \ No newline at end of file 604} \ No newline at end of file
diff --git a/toxav/rtp.h b/toxav/rtp.h
index fa5af9fe..a28ae7bc 100644
--- a/toxav/rtp.h
+++ b/toxav/rtp.h
@@ -44,10 +44,16 @@
44/** 44/**
45 * Payload type identifier. Also used as rtp callback prefix. 45 * Payload type identifier. Also used as rtp callback prefix.
46 */ 46 */
47typedef enum { 47enum {
48 rtp_TypeAudio = 192, 48 rtp_TypeAudio = 192,
49 rtp_TypeVideo 49 rtp_TypeVideo
50} RTPPayloadType; 50};
51
52typedef enum {
53 rtp_StateBad = -1,
54 rtp_StateNormal,
55 rtp_StateGood,
56} RTPTransmissionState;
51 57
52/** 58/**
53 * Standard rtp header. 59 * Standard rtp header.
@@ -108,10 +114,11 @@ typedef struct {
108 114
109 int dest; 115 int dest;
110 116
111 struct RTCPSession_s *rtcp; 117 struct RTCPSession_s *rtcp_session;
112 struct CSession_s *cs; 118 struct CSession_s *cs;
113 Messenger *m; 119 Messenger *m;
114 120
121 RTPTransmissionState tstate;
115} RTPSession; 122} RTPSession;
116 123
117/** 124/**
@@ -150,5 +157,4 @@ int rtp_send_msg ( RTPSession* session, const uint8_t* data, uint16_t length );
150void rtp_free_msg ( RTPSession *session, RTPMessage *msg ); 157void rtp_free_msg ( RTPSession *session, RTPMessage *msg );
151 158
152 159
153
154#endif /* RTP_H */ 160#endif /* RTP_H */
diff --git a/toxav/toxav.c b/toxav/toxav.c
index 721b9d91..0e94e5dd 100644
--- a/toxav/toxav.c
+++ b/toxav/toxav.c
@@ -60,6 +60,12 @@ typedef struct ToxAVCall_s {
60 uint8_t last_self_capabilities; 60 uint8_t last_self_capabilities;
61 uint8_t last_peer_capabilities; 61 uint8_t last_peer_capabilities;
62 62
63 /** Quality control */
64 uint64_t time_audio_good;
65 uint32_t last_bad_audio_bit_rate;
66 uint64_t time_video_good;
67 uint32_t last_bad_video_bit_rate;
68
63 struct ToxAVCall_s *prev; 69 struct ToxAVCall_s *prev;
64 struct ToxAVCall_s *next; 70 struct ToxAVCall_s *next;
65} ToxAVCall; 71} ToxAVCall;
@@ -96,11 +102,13 @@ int callback_end(void* toxav_inst, MSICall* call);
96int callback_error(void* toxav_inst, MSICall* call); 102int callback_error(void* toxav_inst, MSICall* call);
97int callback_capabilites(void* toxav_inst, MSICall* call); 103int callback_capabilites(void* toxav_inst, MSICall* call);
98 104
105bool audio_bitrate_invalid(uint32_t bitrate);
106bool video_bitrate_invalid(uint32_t bitrate);
107void invoke_call_state(ToxAV* av, uint32_t friend_number, uint32_t state);
99ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); 108ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error);
100ToxAVCall* call_get(ToxAV* av, uint32_t friend_number); 109ToxAVCall* call_get(ToxAV* av, uint32_t friend_number);
110void qc_do(ToxAVCall* call);
101void call_remove(ToxAVCall* call); 111void call_remove(ToxAVCall* call);
102bool audio_bitrate_invalid(uint32_t bitrate);
103bool video_bitrate_invalid(uint32_t bitrate);
104bool call_prepare_transmission(ToxAVCall* call); 112bool call_prepare_transmission(ToxAVCall* call);
105void call_kill_transmission(ToxAVCall* call); 113void call_kill_transmission(ToxAVCall* call);
106 114
@@ -220,8 +228,9 @@ void toxav_iterate(ToxAV* av)
220 LOGGED_UNLOCK(av->mutex); 228 LOGGED_UNLOCK(av->mutex);
221 229
222 cs_do(i->cs); 230 cs_do(i->cs);
223 rtp_do(i->rtps[0]); 231 rtp_do(i->rtps[audio_index]);
224 rtp_do(i->rtps[1]); 232 rtp_do(i->rtps[video_index]);
233 qc_do(i);
225 234
226 if (i->last_self_capabilities & msi_CapRAudio) /* Receiving audio */ 235 if (i->last_self_capabilities & msi_CapRAudio) /* Receiving audio */
227 rc = MIN(i->cs->last_packet_frame_duration, rc); 236 rc = MIN(i->cs->last_packet_frame_duration, rc);
@@ -516,8 +525,10 @@ bool toxav_set_audio_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t audio_
516 goto END; 525 goto END;
517 } 526 }
518 527
519 /* NOTE: no need to lock*/ 528 /* Decoding mutex is locked because of quality control */
529 LOGGED_LOCK(call->mutex_decoding);
520 call->audio_bit_rate = audio_bit_rate; 530 call->audio_bit_rate = audio_bit_rate;
531 LOGGED_UNLOCK(call->mutex_decoding);
521 LOGGED_UNLOCK(av->mutex); 532 LOGGED_UNLOCK(av->mutex);
522 533
523END: 534END:
@@ -550,8 +561,10 @@ bool toxav_set_video_bit_rate(ToxAV* av, uint32_t friend_number, uint32_t video_
550 goto END; 561 goto END;
551 } 562 }
552 563
553 /* NOTE: no need to lock*/ 564 /* Decoding mutex is locked because of quality control */
565 LOGGED_LOCK(call->mutex_decoding);
554 call->video_bit_rate = video_bit_rate; 566 call->video_bit_rate = video_bit_rate;
567 LOGGED_UNLOCK(call->mutex_decoding);
555 LOGGED_UNLOCK(av->mutex); 568 LOGGED_UNLOCK(av->mutex);
556 569
557END: 570END:
@@ -813,8 +826,7 @@ int callback_start(void* toxav_inst, MSICall* call)
813 return -1; 826 return -1;
814 } 827 }
815 828
816 if (toxav->scb.first) 829 invoke_call_state(toxav, call->friend_id, call->peer_capabilities);
817 toxav->scb.first(toxav, call->friend_id, call->peer_capabilities, toxav->scb.second);
818 830
819 LOGGED_UNLOCK(toxav->mutex); 831 LOGGED_UNLOCK(toxav->mutex);
820 return 0; 832 return 0;
@@ -825,8 +837,7 @@ int callback_end(void* toxav_inst, MSICall* call)
825 ToxAV* toxav = toxav_inst; 837 ToxAV* toxav = toxav_inst;
826 LOGGED_LOCK(toxav->mutex); 838 LOGGED_LOCK(toxav->mutex);
827 839
828 if (toxav->scb.first) 840 invoke_call_state(toxav, call->friend_id, TOXAV_CALL_STATE_END);
829 toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_END, toxav->scb.second);
830 841
831 call_kill_transmission(call->av_call); 842 call_kill_transmission(call->av_call);
832 call_remove(call->av_call); 843 call_remove(call->av_call);
@@ -840,8 +851,7 @@ int callback_error(void* toxav_inst, MSICall* call)
840 ToxAV* toxav = toxav_inst; 851 ToxAV* toxav = toxav_inst;
841 LOGGED_LOCK(toxav->mutex); 852 LOGGED_LOCK(toxav->mutex);
842 853
843 if (toxav->scb.first) 854 invoke_call_state(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR);
844 toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR, toxav->scb.second);
845 855
846 call_kill_transmission(call->av_call); 856 call_kill_transmission(call->av_call);
847 call_remove(call->av_call); 857 call_remove(call->av_call);
@@ -857,8 +867,7 @@ int callback_capabilites(void* toxav_inst, MSICall* call)
857 867
858 /* TODO modify cs? */ 868 /* TODO modify cs? */
859 869
860 if (toxav->scb.first) 870 invoke_call_state(toxav, call->friend_id, call->peer_capabilities);
861 toxav->scb.first(toxav, call->friend_id, call->peer_capabilities, toxav->scb.second);
862 871
863 LOGGED_UNLOCK(toxav->mutex); 872 LOGGED_UNLOCK(toxav->mutex);
864 return 0; 873 return 0;
@@ -878,6 +887,12 @@ bool video_bitrate_invalid(uint32_t bitrate)
878 return false; 887 return false;
879} 888}
880 889
890void invoke_call_state(ToxAV* av, uint32_t friend_number, uint32_t state)
891{
892 if (av->scb.first)
893 av->scb.first(av, friend_number, state, av->scb.second);
894}
895
881ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error) 896ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
882{ 897{
883 /* Assumes mutex locked */ 898 /* Assumes mutex locked */
@@ -968,6 +983,94 @@ ToxAVCall* call_get(ToxAV* av, uint32_t friend_number)
968 return av->calls[friend_number]; 983 return av->calls[friend_number];
969} 984}
970 985
986void qc_do(ToxAVCall* call)
987{
988 /* Please NOTE: The quality control is rather basic,
989 * advanced algorithms will be applied in the future
990 */
991 switch(call->rtps[audio_index]->tstate) {
992 case rtp_StateBad:
993 LOGGER_DEBUG("Suggesting lower bitrate for audio...");
994 call->time_audio_good = 0;
995 call->last_bad_audio_bit_rate = call->audio_bit_rate;
996 invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_LOWER_AUDIO_BITRATE);
997 break;
998 case rtp_StateGood:
999 if (call->time_audio_good == 0)
1000 call->time_audio_good = current_time_monotonic();
1001 else if (current_time_monotonic() - call->time_audio_good >= 30000 &&
1002 64 > call->audio_bit_rate)
1003 if (call->last_bad_audio_bit_rate > call->audio_bit_rate) {
1004 if (current_time_monotonic() - call->time_audio_good >= 45000)
1005 invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE);
1006 call->last_bad_audio_bit_rate = 0;
1007 } else
1008 invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE);
1009 break;
1010 case rtp_StateNormal:
1011 call->time_audio_good = 0;
1012 break;
1013 }
1014
1015 switch(call->rtps[video_index]->tstate) {
1016 case rtp_StateBad:
1017 LOGGER_DEBUG("Suggesting lower bitrate for video...");
1018 call->time_video_good = 0;
1019 call->last_bad_video_bit_rate = call->video_bit_rate;
1020 invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_LOWER_VIDEO_BITRATE);
1021 break;
1022 case rtp_StateGood:
1023 if (call->time_video_good == 0)
1024 call->time_video_good = current_time_monotonic();
1025 else if (current_time_monotonic() - call->time_video_good >= 30000)
1026 if (call->last_bad_video_bit_rate > call->video_bit_rate) {
1027 if (current_time_monotonic() - call->time_video_good >= 45000)
1028 invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE);
1029 call->last_bad_video_bit_rate = 0;
1030 } else
1031 invoke_call_state(call->av, call->friend_id, TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE);
1032 break;
1033 case rtp_StateNormal:
1034 call->time_video_good = 0;
1035 break;
1036 }
1037
1038}
1039
1040void call_remove(ToxAVCall* call)
1041{
1042 if (call == NULL)
1043 return;
1044
1045 uint32_t friend_id = call->friend_id;
1046 ToxAV* av = call->av;
1047
1048 ToxAVCall* prev = call->prev;
1049 ToxAVCall* next = call->next;
1050
1051 free(call);
1052
1053 if (prev)
1054 prev->next = next;
1055 else if (next)
1056 av->calls_head = next->friend_id;
1057 else goto CLEAR;
1058
1059 if (next)
1060 next->prev = prev;
1061 else if (prev)
1062 av->calls_tail = prev->friend_id;
1063 else goto CLEAR;
1064
1065 av->calls[friend_id] = NULL;
1066 return;
1067
1068CLEAR:
1069 av->calls_head = av->calls_tail = 0;
1070 free(av->calls);
1071 av->calls = NULL;
1072}
1073
971bool call_prepare_transmission(ToxAVCall* call) 1074bool call_prepare_transmission(ToxAVCall* call)
972{ 1075{
973 /* Assumes mutex locked */ 1076 /* Assumes mutex locked */
@@ -1089,37 +1192,3 @@ void call_kill_transmission(ToxAVCall* call)
1089 pthread_mutex_destroy(call->mutex_video_sending); 1192 pthread_mutex_destroy(call->mutex_video_sending);
1090 pthread_mutex_destroy(call->mutex_decoding); 1193 pthread_mutex_destroy(call->mutex_decoding);
1091} 1194}
1092
1093void call_remove(ToxAVCall* call)
1094{
1095 if (call == NULL)
1096 return;
1097
1098 uint32_t friend_id = call->friend_id;
1099 ToxAV* av = call->av;
1100
1101 ToxAVCall* prev = call->prev;
1102 ToxAVCall* next = call->next;
1103
1104 free(call);
1105
1106 if (prev)
1107 prev->next = next;
1108 else if (next)
1109 av->calls_head = next->friend_id;
1110 else goto CLEAR;
1111
1112 if (next)
1113 next->prev = prev;
1114 else if (prev)
1115 av->calls_tail = prev->friend_id;
1116 else goto CLEAR;
1117
1118 av->calls[friend_id] = NULL;
1119 return;
1120
1121CLEAR:
1122 av->calls_head = av->calls_tail = 0;
1123 free(av->calls);
1124 av->calls = NULL;
1125}
diff --git a/toxav/toxav.h b/toxav/toxav.h
index ae95c61b..098075b3 100644
--- a/toxav/toxav.h
+++ b/toxav/toxav.h
@@ -207,21 +207,31 @@ typedef enum TOXAV_CALL_STATE {
207 * The flag that marks that friend is receiving video. 207 * The flag that marks that friend is receiving video.
208 */ 208 */
209 TOXAV_CALL_STATE_RECEIVING_V = 8, 209 TOXAV_CALL_STATE_RECEIVING_V = 8,
210
211 /**
212 * The core will never set TOXAV_CALL_STATE_END or TOXAV_CALL_STATE_ERROR
213 * together with other states.
214 */
215
216 /** 210 /**
217 * The call has finished. This is the final state after which no more state 211 * The call has finished. This is the final state after which no more state
218 * transitions can occur for the call. 212 * transitions can occur for the call.
219 */ 213 */
220 TOXAV_CALL_STATE_END = 16, 214 TOXAV_CALL_STATE_END = 16,
221 /** 215 /**
222 * Sent by the AV core if an error occurred on the remote end. 216 * AV core suggests you to lower bitrate for audio.
217 */
218 TOXAV_CALL_STATE_LOWER_AUDIO_BITRATE = 32,
219 /**
220 * AV core suggests you to lower bitrate for video.
221 */
222 TOXAV_CALL_STATE_LOWER_VIDEO_BITRATE = 64,
223 /**
224 * AV core suggests you to increase bitrate for audio.
225 */
226 TOXAV_CALL_STATE_INCREASE_AUDIO_BITRATE = 128,
227 /**
228 * AV core suggests you to increase bitrate for video.
229 */
230 TOXAV_CALL_STATE_INCREASE_VIDEO_BITRATE = 256,
231 /**
232 * Set by the AV core if an error occurred on the remote end.
223 */ 233 */
224 TOXAV_CALL_STATE_ERROR = 32 234 TOXAV_CALL_STATE_ERROR = 32768
225} TOXAV_CALL_STATE; 235} TOXAV_CALL_STATE;
226/** 236/**
227 * The function type for the `call_state` callback. 237 * The function type for the `call_state` callback.