diff options
Diffstat (limited to 'toxav')
-rw-r--r-- | toxav/av_test.c | 59 | ||||
-rw-r--r-- | toxav/codec.c | 5 | ||||
-rw-r--r-- | toxav/rtp.c | 101 | ||||
-rw-r--r-- | toxav/rtp.h | 16 | ||||
-rw-r--r-- | toxav/toxav.c | 165 | ||||
-rw-r--r-- | toxav/toxav.h | 26 |
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 @@ | |||
80 | typedef struct { | 83 | typedef 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 | ||
85 | struct toxav_thread_data { | 89 | struct toxav_thread_data { |
@@ -91,22 +95,6 @@ struct toxav_thread_data { | |||
91 | const char* vdout = "AV Test"; /* Video output */ | 95 | const char* vdout = "AV Test"; /* Video output */ |
92 | PaStream* adout = NULL; /* Audio output */ | 96 | PaStream* adout = NULL; /* Audio output */ |
93 | 97 | ||
94 | const 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 | } |
118 | void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data) | 106 | void 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 | } |
124 | void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number, | 123 | void 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 | ||
332 | int main (int argc, char** argv) | 331 | int 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 @@ | |||
50 | typedef struct { | 50 | typedef 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 | ||
58 | typedef struct RTCPSession_s { | 58 | typedef 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 */ |
70 | void queue_message(RTPSession *session, RTPMessage *msg); | 73 | void queue_message(RTPSession *session, RTPMessage *msg); |
71 | RTPHeader *parse_header_in ( const uint8_t *payload, int length ); | 74 | RTPHeader *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 | } |
153 | void rtp_do(RTPSession *session) | 151 | void 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 | } |
530 | int handle_rtp_packet ( Messenger *m, int32_t friendnumber, const uint8_t *data, uint32_t length, void *object ) | 541 | int 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 | */ |
47 | typedef enum { | 47 | enum { |
48 | rtp_TypeAudio = 192, | 48 | rtp_TypeAudio = 192, |
49 | rtp_TypeVideo | 49 | rtp_TypeVideo |
50 | } RTPPayloadType; | 50 | }; |
51 | |||
52 | typedef 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 ); | |||
150 | void rtp_free_msg ( RTPSession *session, RTPMessage *msg ); | 157 | void 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); | |||
96 | int callback_error(void* toxav_inst, MSICall* call); | 102 | int callback_error(void* toxav_inst, MSICall* call); |
97 | int callback_capabilites(void* toxav_inst, MSICall* call); | 103 | int callback_capabilites(void* toxav_inst, MSICall* call); |
98 | 104 | ||
105 | bool audio_bitrate_invalid(uint32_t bitrate); | ||
106 | bool video_bitrate_invalid(uint32_t bitrate); | ||
107 | void invoke_call_state(ToxAV* av, uint32_t friend_number, uint32_t state); | ||
99 | ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); | 108 | ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); |
100 | ToxAVCall* call_get(ToxAV* av, uint32_t friend_number); | 109 | ToxAVCall* call_get(ToxAV* av, uint32_t friend_number); |
110 | void qc_do(ToxAVCall* call); | ||
101 | void call_remove(ToxAVCall* call); | 111 | void call_remove(ToxAVCall* call); |
102 | bool audio_bitrate_invalid(uint32_t bitrate); | ||
103 | bool video_bitrate_invalid(uint32_t bitrate); | ||
104 | bool call_prepare_transmission(ToxAVCall* call); | 112 | bool call_prepare_transmission(ToxAVCall* call); |
105 | void call_kill_transmission(ToxAVCall* call); | 113 | void 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 | ||
523 | END: | 534 | END: |
@@ -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 | ||
557 | END: | 570 | END: |
@@ -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 | ||
890 | void 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 | |||
881 | ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error) | 896 | ToxAVCall* 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 | ||
986 | void 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 | |||
1040 | void 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 | |||
1068 | CLEAR: | ||
1069 | av->calls_head = av->calls_tail = 0; | ||
1070 | free(av->calls); | ||
1071 | av->calls = NULL; | ||
1072 | } | ||
1073 | |||
971 | bool call_prepare_transmission(ToxAVCall* call) | 1074 | bool 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 | |||
1093 | void 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 | |||
1121 | CLEAR: | ||
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. |