summaryrefslogtreecommitdiff
path: root/toxav
diff options
context:
space:
mode:
Diffstat (limited to 'toxav')
-rw-r--r--toxav/Makefile.inc56
-rw-r--r--toxav/audio.c439
-rw-r--r--toxav/audio.h64
-rw-r--r--toxav/bwcontroler.c205
-rw-r--r--toxav/bwcontroler.h37
-rw-r--r--toxav/codec.c705
-rw-r--r--toxav/codec.h176
-rw-r--r--toxav/group.c12
-rw-r--r--toxav/msi.c1837
-rw-r--r--toxav/msi.h191
-rw-r--r--toxav/rtp.c688
-rw-r--r--toxav/rtp.h166
-rw-r--r--toxav/toxav.c1506
-rw-r--r--toxav/toxav.h765
-rw-r--r--toxav/toxav_old.c81
-rw-r--r--toxav/video.c267
-rw-r--r--toxav/video.h67
17 files changed, 3759 insertions, 3503 deletions
diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc
index 0b4b869d..083f862f 100644
--- a/toxav/Makefile.inc
+++ b/toxav/Makefile.inc
@@ -1,38 +1,42 @@
1if BUILD_AV 1if BUILD_AV
2 2
3lib_LTLIBRARIES += libtoxav.la 3lib_LTLIBRARIES += libtoxav.la
4libtoxav_la_include_HEADERS = ../toxav/toxav.h 4 libtoxav_la_include_HEADERS = ../toxav/toxav.h
5libtoxav_la_includedir = $(includedir)/tox 5 libtoxav_la_includedir = $(includedir)/tox
6 6
7libtoxav_la_SOURCES = ../toxav/rtp.h \ 7libtoxav_la_SOURCES = ../toxav/rtp.h \
8 ../toxav/rtp.c \ 8 ../toxav/rtp.c \
9 ../toxav/msi.h \ 9 ../toxav/msi.h \
10 ../toxav/msi.c \ 10 ../toxav/msi.c \
11 ../toxav/group.h \ 11 ../toxav/group.h \
12 ../toxav/group.c \ 12 ../toxav/group.c \
13 ../toxav/codec.h \ 13 ../toxav/audio.h \
14 ../toxav/codec.c \ 14 ../toxav/audio.c \
15 ../toxav/toxav.h \ 15 ../toxav/video.h \
16 ../toxav/toxav.c 16 ../toxav/video.c \
17 17 ../toxav/bwcontroler.h \
18 ../toxav/bwcontroler.c \
19 ../toxav/toxav.h \
20 ../toxav/toxav.c \
21 ../toxav/toxav_old.c
18 22
19libtoxav_la_CFLAGS = -I../toxcore \ 23libtoxav_la_CFLAGS = -I../toxcore \
20 -I../toxav \ 24 -I../toxav \
21 $(LIBSODIUM_CFLAGS) \ 25 $(LIBSODIUM_CFLAGS) \
22 $(NACL_CFLAGS) \ 26 $(NACL_CFLAGS) \
23 $(AV_CFLAGS) \ 27 $(AV_CFLAGS) \
24 $(PTHREAD_CFLAGS) 28 $(PTHREAD_CFLAGS)
25 29
26libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \ 30libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \
27 $(LIBSODIUM_LDFLAGS) \ 31 $(LIBSODIUM_LDFLAGS) \
28 $(NACL_LDFLAGS) \ 32 $(NACL_LDFLAGS) \
29 $(EXTRA_LT_LDFLAGS) \ 33 $(EXTRA_LT_LDFLAGS) \
30 $(WINSOCK2_LIBS) 34 $(WINSOCK2_LIBS)
31 35
32libtoxav_la_LIBADD = libtoxcore.la \ 36libtoxav_la_LIBADD = libtoxcore.la \
33 $(LIBSODIUM_LIBS) \ 37 $(LIBSODIUM_LIBS) \
34 $(NACL_LIBS) \ 38 $(NACL_LIBS) \
35 $(PTHREAD_LIBS) \ 39 $(PTHREAD_LIBS) \
36 $(AV_LIBS) 40 $(AV_LIBS)
37 41
38endif \ No newline at end of file 42endif \ No newline at end of file
diff --git a/toxav/audio.c b/toxav/audio.c
new file mode 100644
index 00000000..4f9d3562
--- /dev/null
+++ b/toxav/audio.c
@@ -0,0 +1,439 @@
1/** audio.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 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif /* HAVE_CONFIG_H */
25
26#include <stdlib.h>
27
28#include "audio.h"
29#include "rtp.h"
30
31#include "../toxcore/logger.h"
32
33static struct JitterBuffer *jbuf_new(uint32_t capacity);
34static void jbuf_clear(struct JitterBuffer *q);
35static void jbuf_free(struct JitterBuffer *q);
36static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m);
37static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success);
38OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count);
39bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
40 int32_t *old_br, int32_t *old_sr, int32_t *old_ch);
41bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels);
42
43
44
45ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data)
46{
47 ACSession *ac = calloc(sizeof(ACSession), 1);
48
49 if (!ac) {
50 LOGGER_WARNING("Allocation failed! Application might misbehave!");
51 return NULL;
52 }
53
54 if (create_recursive_mutex(ac->queue_mutex) != 0) {
55 LOGGER_WARNING("Failed to create recursive mutex!");
56 free(ac);
57 return NULL;
58 }
59
60 int status;
61 ac->decoder = opus_decoder_create(48000, 2, &status);
62
63 if (status != OPUS_OK) {
64 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(status));
65 goto BASE_CLEANUP;
66 }
67
68 if (!(ac->j_buf = jbuf_new(3))) {
69 LOGGER_WARNING("Jitter buffer creaton failed!");
70 opus_decoder_destroy(ac->decoder);
71 goto BASE_CLEANUP;
72 }
73
74 /* Initialize encoders with default values */
75 ac->encoder = create_audio_encoder(48000, 48000, 2);
76
77 if (ac->encoder == NULL)
78 goto DECODER_CLEANUP;
79
80 ac->le_bit_rate = 48000;
81 ac->le_sample_rate = 48000;
82 ac->le_channel_count = 2;
83
84 ac->ld_channel_count = 2;
85 ac->ld_sample_rate = 48000;
86 ac->ldrts = 0; /* Make it possible to reconfigure straight away */
87
88 /* These need to be set in order to properly
89 * do error correction with opus */
90 ac->lp_frame_duration = 120;
91 ac->lp_sampling_rate = 48000;
92 ac->lp_channel_count = 1;
93
94 ac->av = av;
95 ac->friend_number = friend_number;
96 ac->acb.first = cb;
97 ac->acb.second = cb_data;
98
99 return ac;
100
101DECODER_CLEANUP:
102 opus_decoder_destroy(ac->decoder);
103 jbuf_free(ac->j_buf);
104BASE_CLEANUP:
105 pthread_mutex_destroy(ac->queue_mutex);
106 free(ac);
107 return NULL;
108}
109void ac_kill(ACSession *ac)
110{
111 if (!ac)
112 return;
113
114 opus_encoder_destroy(ac->encoder);
115 opus_decoder_destroy(ac->decoder);
116 jbuf_free(ac->j_buf);
117
118 pthread_mutex_destroy(ac->queue_mutex);
119
120 LOGGER_DEBUG("Terminated audio handler: %p", ac);
121 free(ac);
122}
123void ac_iterate(ACSession *ac)
124{
125 if (!ac)
126 return;
127
128 /* TODO fix this and jitter buffering */
129
130 /* Enough space for the maximum frame size (120 ms 48 KHz stereo audio) */
131 int16_t tmp[5760 * 2];
132
133 struct RTPMessage *msg;
134 int rc = 0;
135
136 pthread_mutex_lock(ac->queue_mutex);
137
138 while ((msg = jbuf_read(ac->j_buf, &rc)) || rc == 2) {
139 pthread_mutex_unlock(ac->queue_mutex);
140
141 if (rc == 2) {
142 LOGGER_DEBUG("OPUS correction");
143 int fs = (ac->lp_sampling_rate * ac->lp_frame_duration) / 1000;
144 rc = opus_decode(ac->decoder, NULL, 0, tmp, fs, 1);
145 } else {
146 /* Get values from packet and decode. */
147 /* NOTE: This didn't work very well
148 rc = convert_bw_to_sampling_rate(opus_packet_get_bandwidth(msg->data));
149 if (rc != -1) {
150 cs->last_packet_sampling_rate = rc;
151 } else {
152 LOGGER_WARNING("Failed to load packet values!");
153 rtp_free_msg(msg);
154 continue;
155 }*/
156
157
158 /* Pick up sampling rate from packet */
159 memcpy(&ac->lp_sampling_rate, msg->data, 4);
160 ac->lp_sampling_rate = ntohl(ac->lp_sampling_rate);
161
162 ac->lp_channel_count = opus_packet_get_nb_channels(msg->data + 4);
163
164 /** NOTE: even though OPUS supports decoding mono frames with stereo decoder and vice versa,
165 * it didn't work quite well.
166 */
167 if (!reconfigure_audio_decoder(ac, ac->lp_sampling_rate, ac->lp_channel_count)) {
168 LOGGER_WARNING("Failed to reconfigure decoder!");
169 free(msg);
170 continue;
171 }
172
173 rc = opus_decode(ac->decoder, msg->data + 4, msg->len - 4, tmp, 5760, 0);
174 free(msg);
175 }
176
177 if (rc < 0) {
178 LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
179 } else if (ac->acb.first) {
180 ac->lp_frame_duration = (rc * 1000) / ac->lp_sampling_rate;
181
182 ac->acb.first(ac->av, ac->friend_number, tmp, rc, ac->lp_channel_count,
183 ac->lp_sampling_rate, ac->acb.second);
184 }
185
186 return;
187 }
188
189 pthread_mutex_unlock(ac->queue_mutex);
190}
191int ac_queue_message(void *acp, struct RTPMessage *msg)
192{
193 if (!acp || !msg)
194 return -1;
195
196 if ((msg->header.pt & 0x7f) == (rtp_TypeAudio + 2) % 128) {
197 LOGGER_WARNING("Got dummy!");
198 free(msg);
199 return 0;
200 }
201
202 if ((msg->header.pt & 0x7f) != rtp_TypeAudio % 128) {
203 LOGGER_WARNING("Invalid payload type!");
204 free(msg);
205 return -1;
206 }
207
208 ACSession *ac = acp;
209
210 pthread_mutex_lock(ac->queue_mutex);
211 int rc = jbuf_write(ac->j_buf, msg);
212 pthread_mutex_unlock(ac->queue_mutex);
213
214 if (rc == -1) {
215 LOGGER_WARNING("Could not queue the message!");
216 free(msg);
217 return -1;
218 }
219
220 return 0;
221}
222int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels)
223{
224 if (!ac || !reconfigure_audio_encoder(&ac->encoder, bit_rate,
225 sampling_rate, channels,
226 &ac->le_bit_rate,
227 &ac->le_sample_rate,
228 &ac->le_channel_count))
229 return -1;
230
231 return 0;
232}
233
234
235
236struct JitterBuffer {
237 struct RTPMessage **queue;
238 uint32_t size;
239 uint32_t capacity;
240 uint16_t bottom;
241 uint16_t top;
242};
243
244static struct JitterBuffer *jbuf_new(uint32_t capacity)
245{
246 unsigned int size = 1;
247
248 while (size <= (capacity * 4)) {
249 size *= 2;
250 }
251
252 struct JitterBuffer *q;
253
254 if (!(q = calloc(sizeof(struct JitterBuffer), 1))) return NULL;
255
256 if (!(q->queue = calloc(sizeof(struct RTPMessage *), size))) {
257 free(q);
258 return NULL;
259 }
260
261 q->size = size;
262 q->capacity = capacity;
263 return q;
264}
265static void jbuf_clear(struct JitterBuffer *q)
266{
267 for (; q->bottom != q->top; ++q->bottom) {
268 if (q->queue[q->bottom % q->size]) {
269 free(q->queue[q->bottom % q->size]);
270 q->queue[q->bottom % q->size] = NULL;
271 }
272 }
273}
274static void jbuf_free(struct JitterBuffer *q)
275{
276 if (!q) return;
277
278 jbuf_clear(q);
279 free(q->queue);
280 free(q);
281}
282static int jbuf_write(struct JitterBuffer *q, struct RTPMessage *m)
283{
284 uint16_t sequnum = m->header.sequnum;
285
286 unsigned int num = sequnum % q->size;
287
288 if ((uint32_t)(sequnum - q->bottom) > q->size) {
289 LOGGER_DEBUG("Clearing filled jitter buffer: %p", q);
290
291 jbuf_clear(q);
292 q->bottom = sequnum - q->capacity;
293 q->queue[num] = m;
294 q->top = sequnum + 1;
295 return 0;
296 }
297
298 if (q->queue[num])
299 return -1;
300
301 q->queue[num] = m;
302
303 if ((sequnum - q->bottom) >= (q->top - q->bottom))
304 q->top = sequnum + 1;
305
306 return 0;
307}
308static struct RTPMessage *jbuf_read(struct JitterBuffer *q, int32_t *success)
309{
310 if (q->top == q->bottom) {
311 *success = 0;
312 return NULL;
313 }
314
315 unsigned int num = q->bottom % q->size;
316
317 if (q->queue[num]) {
318 struct RTPMessage *ret = q->queue[num];
319 q->queue[num] = NULL;
320 ++q->bottom;
321 *success = 1;
322 return ret;
323 }
324
325 if ((uint32_t)(q->top - q->bottom) > q->capacity) {
326 ++q->bottom;
327 *success = 2;
328 return NULL;
329 }
330
331 *success = 0;
332 return NULL;
333}
334OpusEncoder *create_audio_encoder (int32_t bit_rate, int32_t sampling_rate, int32_t channel_count)
335{
336 int status = OPUS_OK;
337 OpusEncoder *rc = opus_encoder_create(sampling_rate, channel_count, OPUS_APPLICATION_VOIP, &status);
338
339 if (status != OPUS_OK) {
340 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(status));
341 return NULL;
342 }
343
344 status = opus_encoder_ctl(rc, OPUS_SET_BITRATE(bit_rate));
345
346 if (status != OPUS_OK) {
347 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
348 goto FAILURE;
349 }
350
351 /* Enable in-band forward error correction in codec */
352 status = opus_encoder_ctl(rc, OPUS_SET_INBAND_FEC(1));
353
354 if (status != OPUS_OK) {
355 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
356 goto FAILURE;
357 }
358
359 /* Make codec resistant to up to 10% packet loss
360 * NOTE This could also be adjusted on the fly, rather than hard-coded,
361 * with feedback from the receiving client.
362 */
363 status = opus_encoder_ctl(rc, OPUS_SET_PACKET_LOSS_PERC(10));
364
365 if (status != OPUS_OK) {
366 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
367 goto FAILURE;
368 }
369
370 /* Set algorithm to the highest complexity, maximizing compression */
371 status = opus_encoder_ctl(rc, OPUS_SET_COMPLEXITY(10));
372
373 if (status != OPUS_OK) {
374 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
375 goto FAILURE;
376 }
377
378 return rc;
379
380FAILURE:
381 opus_encoder_destroy(rc);
382 return NULL;
383}
384bool reconfigure_audio_encoder(OpusEncoder **e, int32_t new_br, int32_t new_sr, uint8_t new_ch,
385 int32_t *old_br, int32_t *old_sr, int32_t *old_ch)
386{
387 /* Values are checked in toxav.c */
388 if (*old_sr != new_sr || *old_ch != new_ch) {
389 OpusEncoder *new_encoder = create_audio_encoder(new_br, new_sr, new_ch);
390
391 if (new_encoder == NULL)
392 return false;
393
394 opus_encoder_destroy(*e);
395 *e = new_encoder;
396 } else if (*old_br == new_br)
397 return true; /* Nothing changed */
398 else {
399 int status = opus_encoder_ctl(*e, OPUS_SET_BITRATE(new_br));
400
401 if (status != OPUS_OK) {
402 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(status));
403 return false;
404 }
405 }
406
407 *old_br = new_br;
408 *old_sr = new_sr;
409 *old_ch = new_ch;
410
411 LOGGER_DEBUG ("Reconfigured audio encoder br: %d sr: %d cc:%d", new_br, new_sr, new_ch);
412 return true;
413}
414bool reconfigure_audio_decoder(ACSession *ac, int32_t sampling_rate, int8_t channels)
415{
416 if (sampling_rate != ac->ld_sample_rate || channels != ac->ld_channel_count) {
417 if (current_time_monotonic() - ac->ldrts < 500)
418 return false;
419
420 int status;
421 OpusDecoder *new_dec = opus_decoder_create(sampling_rate, channels, &status);
422
423 if (status != OPUS_OK) {
424 LOGGER_ERROR("Error while starting audio decoder(%d %d): %s", sampling_rate, channels, opus_strerror(status));
425 return false;
426 }
427
428 ac->ld_sample_rate = sampling_rate;
429 ac->ld_channel_count = channels;
430 ac->ldrts = current_time_monotonic();
431
432 opus_decoder_destroy(ac->decoder);
433 ac->decoder = new_dec;
434
435 LOGGER_DEBUG("Reconfigured audio decoder sr: %d cc: %d", sampling_rate, channels);
436 }
437
438 return true;
439}
diff --git a/toxav/audio.h b/toxav/audio.h
new file mode 100644
index 00000000..b1db7448
--- /dev/null
+++ b/toxav/audio.h
@@ -0,0 +1,64 @@
1/** audio.h
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 */
21
22#ifndef AUDIO_H
23#define AUDIO_H
24
25#include <opus.h>
26#include <pthread.h>
27
28#include "toxav.h"
29
30#include "../toxcore/util.h"
31
32struct RTPMessage;
33
34typedef struct ACSession_s {
35 /* encoding */
36 OpusEncoder *encoder;
37 int32_t le_sample_rate; /* Last encoder sample rate */
38 int32_t le_channel_count; /* Last encoder channel count */
39 int32_t le_bit_rate; /* Last encoder bit rate */
40
41 /* decoding */
42 OpusDecoder *decoder;
43 int32_t lp_channel_count; /* Last packet channel count */
44 int32_t lp_sampling_rate; /* Last packet sample rate */
45 int32_t lp_frame_duration; /* Last packet frame duration */
46 int32_t ld_sample_rate; /* Last decoder sample rate */
47 int32_t ld_channel_count; /* Last decoder channel count */
48 uint64_t ldrts; /* Last decoder reconfiguration time stamp */
49 void *j_buf;
50
51 pthread_mutex_t queue_mutex[1];
52
53 ToxAV *av;
54 uint32_t friend_number;
55 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
56} ACSession;
57
58ACSession *ac_new(ToxAV *av, uint32_t friend_number, toxav_audio_receive_frame_cb *cb, void *cb_data);
59void ac_kill(ACSession *ac);
60void ac_iterate(ACSession *ac);
61int ac_queue_message(void *acp, struct RTPMessage *msg);
62int ac_reconfigure_encoder(ACSession *ac, int32_t bit_rate, int32_t sampling_rate, uint8_t channels);
63
64#endif /* AUDIO_H */
diff --git a/toxav/bwcontroler.c b/toxav/bwcontroler.c
new file mode 100644
index 00000000..2530e7fd
--- /dev/null
+++ b/toxav/bwcontroler.c
@@ -0,0 +1,205 @@
1/** bwcontroler.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 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif /* HAVE_CONFIG_H */
25
26#include <assert.h>
27#include "bwcontroler.h"
28#include "../toxcore/logger.h"
29#include "../toxcore/util.h"
30
31#define BWC_PACKET_ID 196
32#define BWC_SEND_INTERVAL_MS 1000
33#define BWC_REFRESH_INTERVAL_MS 10000
34#define BWC_AVG_PKT_COUNT 20
35
36/**
37 *
38 */
39
40struct BWControler_s {
41 void (*mcb) (BWControler *, uint32_t, float, void *);
42 void *mcb_data;
43
44 Messenger *m;
45 uint32_t friend_number;
46
47 struct {
48 uint32_t lru; /* Last recv update time stamp */
49 uint32_t lsu; /* Last sent update time stamp */
50 uint32_t lfu; /* Last refresh time stamp */
51
52 uint32_t lost;
53 uint32_t recv;
54 } cycle;
55
56 struct {
57 uint32_t rb_s[BWC_AVG_PKT_COUNT];
58 RingBuffer *rb;
59 } rcvpkt; /* To calculate average received packet */
60};
61
62int bwc_handle_data(Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
63void send_update(BWControler *bwc);
64
65BWControler *bwc_new(Messenger *m, uint32_t friendnumber,
66 void (*mcb) (BWControler *, uint32_t, float, void *),
67 void *udata)
68{
69 BWControler *retu = calloc(sizeof(struct BWControler_s), 1);
70
71 retu->mcb = mcb;
72 retu->mcb_data = udata;
73 retu->m = m;
74 retu->friend_number = friendnumber;
75 retu->cycle.lsu = retu->cycle.lfu = current_time_monotonic();
76 retu->rcvpkt.rb = rb_new(BWC_AVG_PKT_COUNT);
77
78 /* Fill with zeros */
79 int i = 0;
80 for (; i < BWC_AVG_PKT_COUNT; i ++)
81 rb_write(retu->rcvpkt.rb, retu->rcvpkt.rb_s + i);
82
83 m_callback_rtp_packet(m, friendnumber, BWC_PACKET_ID, bwc_handle_data, retu);
84
85 return retu;
86}
87void bwc_kill(BWControler *bwc)
88{
89 if (!bwc)
90 return;
91
92 m_callback_rtp_packet(bwc->m, bwc->friend_number, BWC_PACKET_ID, NULL, NULL);
93
94 rb_kill(bwc->rcvpkt.rb);
95 free(bwc);
96}
97void bwc_feed_avg(BWControler* bwc, uint32_t bytes)
98{
99 uint32_t *p;
100
101 rb_read(bwc->rcvpkt.rb, (void**) &p);
102 rb_write(bwc->rcvpkt.rb, p);
103
104 *p = bytes;
105}
106void bwc_add_lost(BWControler *bwc, uint32_t bytes)
107{
108 if (!bwc)
109 return;
110
111 if (!bytes) {
112 uint32_t* t_avg[BWC_AVG_PKT_COUNT], c = 1;
113
114 rb_data(bwc->rcvpkt.rb, (void**) t_avg);
115
116 int i = 0;
117 for (; i < BWC_AVG_PKT_COUNT; i ++) {
118 bytes += *(t_avg[i]);
119
120 if (*(t_avg[i]))
121 c++;
122 }
123
124 bytes /= c;
125 }
126
127 bwc->cycle.lost += bytes;
128 send_update(bwc);
129}
130void bwc_add_recv(BWControler *bwc, uint32_t bytes)
131{
132 if (!bwc || !bytes)
133 return;
134
135 bwc->cycle.recv += bytes;
136 send_update(bwc);
137}
138
139
140struct BWCMessage {
141 uint32_t lost;
142 uint32_t recv;
143};
144
145void send_update(BWControler *bwc)
146{
147 if (current_time_monotonic() - bwc->cycle.lfu > BWC_REFRESH_INTERVAL_MS) {
148
149 bwc->cycle.lost /= 10;
150 bwc->cycle.recv /= 10;
151 bwc->cycle.lfu = current_time_monotonic();
152 }
153 else if (current_time_monotonic() - bwc->cycle.lsu > BWC_SEND_INTERVAL_MS) {
154
155 if (bwc->cycle.lost)
156 {
157 LOGGER_DEBUG ("%p Sent update rcv: %u lost: %u",
158 bwc, bwc->cycle.recv, bwc->cycle.lost);
159
160 uint8_t p_msg[sizeof(struct BWCMessage) + 1];
161 struct BWCMessage* b_msg = (struct BWCMessage*)(p_msg + 1);
162
163 p_msg[0] = BWC_PACKET_ID;
164 b_msg->lost = htonl(bwc->cycle.lost);
165 b_msg->recv = htonl(bwc->cycle.recv);
166
167 if (-1 == send_custom_lossy_packet(bwc->m, bwc->friend_number, p_msg, sizeof(p_msg)))
168 LOGGER_WARNING("BWC send failed (len: %d)! std error: %s", sizeof(p_msg), strerror(errno));
169 }
170
171 bwc->cycle.lsu = current_time_monotonic();
172 }
173}
174int on_update (BWControler *bwc, struct BWCMessage *msg)
175{
176 LOGGER_DEBUG ("%p Got update from peer", bwc);
177
178 /* Peer must respect time boundary */
179 if (current_time_monotonic() < bwc->cycle.lru + BWC_SEND_INTERVAL_MS) {
180 LOGGER_DEBUG("%p Rejecting extra update", bwc);
181 return -1;
182 }
183
184 bwc->cycle.lru = current_time_monotonic();
185
186 msg->recv = ntohl(msg->recv);
187 msg->lost = ntohl(msg->lost);
188
189 LOGGER_DEBUG ("recved: %u lost: %u", msg->recv, msg->lost);
190
191 if (msg->lost && bwc->mcb)
192 bwc->mcb(bwc, bwc->friend_number,
193 ((float) (msg->lost) / (msg->recv + msg->lost)),
194 bwc->mcb_data);
195
196 return 0;
197}
198int bwc_handle_data(Messenger* m, uint32_t friendnumber, const uint8_t* data, uint16_t length, void* object)
199{
200 if (length - 1 != sizeof(struct BWCMessage))
201 return -1;
202
203 /* NOTE the data is mutable */
204 return on_update(object, (struct BWCMessage *) (data + 1));
205}
diff --git a/toxav/bwcontroler.h b/toxav/bwcontroler.h
new file mode 100644
index 00000000..53b07d38
--- /dev/null
+++ b/toxav/bwcontroler.h
@@ -0,0 +1,37 @@
1/** bwcontroler.h
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 */
21
22#ifndef BWCONROLER_H
23#define BWCONROLER_H
24#include "../toxcore/Messenger.h"
25
26typedef struct BWControler_s BWControler;
27
28BWControler *bwc_new(Messenger *m, uint32_t friendnumber,
29 void (*mcb) (BWControler *, uint32_t, float, void *),
30 void *udata);
31void bwc_kill(BWControler *bwc);
32
33void bwc_feed_avg(BWControler *bwc, uint32_t bytes);
34void bwc_add_lost(BWControler *bwc, uint32_t bytes);
35void bwc_add_recv(BWControler *bwc, uint32_t bytes);
36
37#endif /* BWCONROLER_H */
diff --git a/toxav/codec.c b/toxav/codec.c
deleted file mode 100644
index 8940aa25..00000000
--- a/toxav/codec.c
+++ /dev/null
@@ -1,705 +0,0 @@
1/** codec.c
2 *
3 * Audio and video codec intitialization, encoding/decoding and playback
4 *
5 * Copyright (C) 2013 Tox project All Rights Reserved.
6 *
7 * This file is part of Tox.
8 *
9 * Tox is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * Tox is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif /* HAVE_CONFIG_H */
28
29#include "codec.h"
30#include "../toxcore/logger.h"
31#include "../toxcore/util.h"
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <math.h>
36#include <assert.h>
37#include <time.h>
38
39#include "msi.h"
40#include "rtp.h"
41
42/* Good quality encode. */
43#define MAX_DECODE_TIME_US 0
44
45// TODO this has to be exchanged in msi
46#define MAX_VIDEOFRAME_SIZE 0x40000 /* 256KiB */
47#define VIDEOFRAME_PIECE_SIZE 0x500 /* 1.25 KiB*/
48#define VIDEOFRAME_HEADER_SIZE 0x2
49
50/* FIXME: Might not be enough */
51#define VIDEO_DECODE_BUFFER_SIZE 20
52
53#define ARRAY(TYPE__) struct { uint16_t size; TYPE__ data[]; }
54
55typedef ARRAY(uint8_t) Payload;
56
57typedef struct {
58 uint16_t size; /* Max size */
59 uint16_t start;
60 uint16_t end;
61 Payload **packets;
62} PayloadBuffer;
63
64static _Bool buffer_full(const PayloadBuffer *b)
65{
66 return (b->end + 1) % b->size == b->start;
67}
68
69static _Bool buffer_empty(const PayloadBuffer *b)
70{
71 return b->end == b->start;
72}
73
74static void buffer_write(PayloadBuffer *b, Payload *p)
75{
76 b->packets[b->end] = p;
77 b->end = (b->end + 1) % b->size;
78
79 if (b->end == b->start) b->start = (b->start + 1) % b->size; /* full, overwrite */
80}
81
82static void buffer_read(PayloadBuffer *b, Payload **p)
83{
84 *p = b->packets[b->start];
85 b->start = (b->start + 1) % b->size;
86}
87
88static void buffer_clear(PayloadBuffer *b)
89{
90 while (!buffer_empty(b)) {
91 Payload *p;
92 buffer_read(b, &p);
93 free(p);
94 }
95}
96
97static PayloadBuffer *buffer_new(int size)
98{
99 PayloadBuffer *buf = calloc(sizeof(PayloadBuffer), 1);
100
101 if (!buf) return NULL;
102
103 buf->size = size + 1; /* include empty elem */
104
105 if (!(buf->packets = calloc(buf->size, sizeof(Payload *)))) {
106 free(buf);
107 return NULL;
108 }
109
110 return buf;
111}
112
113static void buffer_free(PayloadBuffer *b)
114{
115 if (b) {
116 buffer_clear(b);
117 free(b->packets);
118 free(b);
119 }
120}
121
122/* JITTER BUFFER WORK */
123typedef struct _JitterBuffer {
124 RTPMessage **queue;
125 uint32_t size;
126 uint32_t capacity;
127 uint16_t bottom;
128 uint16_t top;
129} JitterBuffer;
130
131static JitterBuffer *jbuf_new(uint32_t capacity)
132{
133 unsigned int size = 1;
134
135 while (size <= (capacity * 4)) {
136 size *= 2;
137 }
138
139 JitterBuffer *q;
140
141 if ( !(q = calloc(sizeof(JitterBuffer), 1)) ) return NULL;
142
143 if (!(q->queue = calloc(sizeof(RTPMessage *), size))) {
144 free(q);
145 return NULL;
146 }
147
148 q->size = size;
149 q->capacity = capacity;
150 return q;
151}
152
153static void jbuf_clear(JitterBuffer *q)
154{
155 for (; q->bottom != q->top; ++q->bottom) {
156 if (q->queue[q->bottom % q->size]) {
157 rtp_free_msg(NULL, q->queue[q->bottom % q->size]);
158 q->queue[q->bottom % q->size] = NULL;
159 }
160 }
161}
162
163static void jbuf_free(JitterBuffer *q)
164{
165 if (!q) return;
166
167 jbuf_clear(q);
168 free(q->queue);
169 free(q);
170}
171
172static int jbuf_write(JitterBuffer *q, RTPMessage *m)
173{
174 uint16_t sequnum = m->header->sequnum;
175
176 unsigned int num = sequnum % q->size;
177
178 if ((uint32_t)(sequnum - q->bottom) > q->size) {
179 jbuf_clear(q);
180 q->bottom = sequnum - q->capacity;
181 q->queue[num] = m;
182 q->top = sequnum + 1;
183 return 0;
184 }
185
186 if (q->queue[num])
187 return -1;
188
189 q->queue[num] = m;
190
191 if ((sequnum - q->bottom) >= (q->top - q->bottom))
192 q->top = sequnum + 1;
193
194 return 0;
195}
196
197/* Success is 0 when there is nothing to dequeue,
198 * 1 when there's a good packet,
199 * 2 when there's a lost packet */
200static RTPMessage *jbuf_read(JitterBuffer *q, int32_t *success)
201{
202 if (q->top == q->bottom) {
203 *success = 0;
204 return NULL;
205 }
206
207 unsigned int num = q->bottom % q->size;
208
209 if (q->queue[num]) {
210 RTPMessage *ret = q->queue[num];
211 q->queue[num] = NULL;
212 ++q->bottom;
213 *success = 1;
214 return ret;
215 }
216
217 if ((uint32_t)(q->top - q->bottom) > q->capacity) {
218 ++q->bottom;
219 *success = 2;
220 return NULL;
221 }
222
223 *success = 0;
224 return NULL;
225}
226
227static int init_video_decoder(CSSession *cs)
228{
229 int rc = vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION);
230
231 if ( rc != VPX_CODEC_OK) {
232 LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
233 return -1;
234 }
235
236 return 0;
237}
238
239static int init_audio_decoder(CSSession *cs)
240{
241 int rc;
242 cs->audio_decoder = opus_decoder_create(cs->audio_decoder_sample_rate, cs->audio_decoder_channels, &rc );
243
244 if ( rc != OPUS_OK ) {
245 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc));
246 return -1;
247 }
248
249 return 0;
250}
251
252static int init_video_encoder(CSSession *cs, uint16_t max_width, uint16_t max_height, uint32_t video_bitrate)
253{
254 vpx_codec_enc_cfg_t cfg;
255 int rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
256
257 if (rc != VPX_CODEC_OK) {
258 LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc));
259 return -1;
260 }
261
262 cfg.rc_target_bitrate = video_bitrate;
263 cfg.g_w = max_width;
264 cfg.g_h = max_height;
265 cfg.g_pass = VPX_RC_ONE_PASS;
266 cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
267 cfg.g_lag_in_frames = 0;
268 cfg.kf_min_dist = 0;
269 cfg.kf_max_dist = 48;
270 cfg.kf_mode = VPX_KF_AUTO;
271
272 rc = vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION);
273
274 if ( rc != VPX_CODEC_OK) {
275 LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
276 return -1;
277 }
278
279 rc = vpx_codec_control(&cs->v_encoder, VP8E_SET_CPUUSED, 8);
280
281 if ( rc != VPX_CODEC_OK) {
282 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
283 return -1;
284 }
285
286 cs->max_width = max_width;
287 cs->max_height = max_height;
288 cs->video_bitrate = video_bitrate;
289
290 return 0;
291}
292
293static int init_audio_encoder(CSSession *cs)
294{
295 int rc = OPUS_OK;
296 cs->audio_encoder = opus_encoder_create(cs->audio_encoder_sample_rate,
297 cs->audio_encoder_channels, OPUS_APPLICATION_VOIP, &rc);
298
299 if ( rc != OPUS_OK ) {
300 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc));
301 return -1;
302 }
303
304 rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_encoder_bitrate));
305
306 if ( rc != OPUS_OK ) {
307 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
308 return -1;
309 }
310
311 /* Enable in-band forward error correction in codec */
312 rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_INBAND_FEC(1));
313
314 if ( rc != OPUS_OK ) {
315 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
316 return -1;
317 }
318
319 /* Make codec resistant to up to 10% packet loss */
320 rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_PACKET_LOSS_PERC(10));
321
322 if ( rc != OPUS_OK ) {
323 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
324 return -1;
325 }
326
327 /* Set algorithm to the highest complexity, maximizing compression */
328 rc = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
329
330 if ( rc != OPUS_OK ) {
331 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
332 return -1;
333 }
334
335 return 0;
336}
337
338/* PUBLIC */
339int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length)
340{
341 if (!cs || !length || length > cs->max_video_frame_size) {
342 LOGGER_ERROR("Invalid CodecState or video frame size: %u", length);
343 return cs_ErrorSplittingVideoPayload;
344 }
345
346 cs->split_video_frame[0] = cs->frameid_out++;
347 cs->split_video_frame[1] = 0;
348 cs->processing_video_frame = payload;
349 cs->processing_video_frame_size = length;
350
351 return ((length - 1) / cs->video_frame_piece_size) + 1;
352}
353
354const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size)
355{
356 if (!cs || !size) return NULL;
357
358 if (cs->processing_video_frame_size > cs->video_frame_piece_size) {
359 memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE,
360 cs->processing_video_frame,
361 cs->video_frame_piece_size);
362
363 cs->processing_video_frame += cs->video_frame_piece_size;
364 cs->processing_video_frame_size -= cs->video_frame_piece_size;
365
366 *size = cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE;
367 } else {
368 memcpy(cs->split_video_frame + VIDEOFRAME_HEADER_SIZE,
369 cs->processing_video_frame,
370 cs->processing_video_frame_size);
371
372 *size = cs->processing_video_frame_size + VIDEOFRAME_HEADER_SIZE;
373 }
374
375 cs->split_video_frame[1]++;
376
377 return cs->split_video_frame;
378}
379
380void cs_do(CSSession *cs)
381{
382 /* Codec session should always be protected by call mutex so no need to check for cs validity
383 */
384
385 if (!cs) return;
386
387 Payload *p;
388 int rc;
389
390 int success = 0;
391
392 pthread_mutex_lock(cs->queue_mutex);
393 RTPMessage *msg;
394
395 while ((msg = jbuf_read(cs->j_buf, &success)) || success == 2) {
396 pthread_mutex_unlock(cs->queue_mutex);
397
398 uint16_t fsize = ((cs->audio_decoder_sample_rate * cs->audio_decoder_frame_duration) / 1000);
399 int16_t tmp[fsize * cs->audio_decoder_channels];
400
401 if (success == 2) {
402 rc = opus_decode(cs->audio_decoder, 0, 0, tmp, fsize, 1);
403 } else {
404 rc = opus_decode(cs->audio_decoder, msg->data, msg->length, tmp, fsize, 0);
405 rtp_free_msg(NULL, msg);
406 }
407
408 if (rc < 0) {
409 LOGGER_WARNING("Decoding error: %s", opus_strerror(rc));
410 } else if (cs->acb.first) {
411 /* Play */
412 cs->acb.first(cs->agent, cs->call_idx, tmp, rc, cs->acb.second);
413 }
414
415 pthread_mutex_lock(cs->queue_mutex);
416 }
417
418 if (cs->vbuf_raw && !buffer_empty(cs->vbuf_raw)) {
419 /* Decode video */
420 buffer_read(cs->vbuf_raw, &p);
421
422 /* Leave space for (possibly) other thread to queue more data after we read it here */
423 pthread_mutex_unlock(cs->queue_mutex);
424
425 rc = vpx_codec_decode(&cs->v_decoder, p->data, p->size, NULL, MAX_DECODE_TIME_US);
426 free(p);
427
428 if (rc != VPX_CODEC_OK) {
429 LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc));
430 } else {
431 vpx_codec_iter_t iter = NULL;
432 vpx_image_t *dest = vpx_codec_get_frame(&cs->v_decoder, &iter);
433
434 /* Play decoded images */
435 for (; dest; dest = vpx_codec_get_frame(&cs->v_decoder, &iter)) {
436 if (cs->vcb.first)
437 cs->vcb.first(cs->agent, cs->call_idx, dest, cs->vcb.second);
438
439 vpx_img_free(dest);
440 }
441 }
442
443 return;
444 }
445
446 pthread_mutex_unlock(cs->queue_mutex);
447}
448
449int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height)
450{
451 vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
452
453 if (cfg.g_w == width && cfg.g_h == height)
454 return 0;
455
456 if (width * height > cs->max_width * cs->max_height) {
457 vpx_codec_ctx_t v_encoder = cs->v_encoder;
458
459 if (init_video_encoder(cs, width, height, cs->video_bitrate) == -1) {
460 cs->v_encoder = v_encoder;
461 return cs_ErrorSettingVideoResolution;
462 }
463
464 vpx_codec_destroy(&v_encoder);
465 return 0;
466 }
467
468 LOGGER_DEBUG("New video resolution: %u %u", width, height);
469 cfg.g_w = width;
470 cfg.g_h = height;
471 int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg);
472
473 if ( rc != VPX_CODEC_OK) {
474 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
475 return cs_ErrorSettingVideoResolution;
476 }
477
478 return 0;
479}
480
481int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate)
482{
483 vpx_codec_enc_cfg_t cfg = *cs->v_encoder.config.enc;
484
485 if (cfg.rc_target_bitrate == video_bitrate)
486 return 0;
487
488 LOGGER_DEBUG("New video bitrate: %u", video_bitrate);
489 cfg.rc_target_bitrate = video_bitrate;
490 int rc = vpx_codec_enc_config_set(&cs->v_encoder, &cfg);
491
492 if ( rc != VPX_CODEC_OK) {
493 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
494 return cs_ErrorSettingVideoBitrate;
495 }
496
497 cs->video_bitrate = video_bitrate;
498 return 0;
499}
500
501CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video)
502{
503 CSSession *cs = calloc(sizeof(CSSession), 1);
504
505 if (!cs) {
506 LOGGER_WARNING("Allocation failed! Application might misbehave!");
507 return NULL;
508 }
509
510 if (create_recursive_mutex(cs->queue_mutex) != 0) {
511 LOGGER_WARNING("Failed to create recursive mutex!");
512 free(cs);
513 return NULL;
514 }
515
516 if ( !(cs->j_buf = jbuf_new(jbuf_size)) ) {
517 LOGGER_WARNING("Jitter buffer creaton failed!");
518 goto error;
519 }
520
521 cs->audio_encoder_bitrate = cs_self->audio_bitrate;
522 cs->audio_encoder_sample_rate = cs_self->audio_sample_rate;
523 cs->audio_encoder_channels = cs_self->audio_channels;
524 cs->audio_encoder_frame_duration = cs_self->audio_frame_duration;
525
526 cs->audio_decoder_bitrate = cs_peer->audio_bitrate;
527 cs->audio_decoder_sample_rate = cs_peer->audio_sample_rate;
528 cs->audio_decoder_channels = cs_peer->audio_channels;
529 cs->audio_decoder_frame_duration = cs_peer->audio_frame_duration;
530
531
532 cs->capabilities |= ( 0 == init_audio_encoder(cs) ) ? cs_AudioEncoding : 0;
533 cs->capabilities |= ( 0 == init_audio_decoder(cs) ) ? cs_AudioDecoding : 0;
534
535 if ( !(cs->capabilities & cs_AudioEncoding) || !(cs->capabilities & cs_AudioDecoding) ) goto error;
536
537 if ((cs->support_video = has_video)) {
538 cs->max_video_frame_size = MAX_VIDEOFRAME_SIZE;
539 cs->video_frame_piece_size = VIDEOFRAME_PIECE_SIZE;
540
541 cs->capabilities |= ( 0 == init_video_encoder(cs, cs_self->max_video_width,
542 cs_self->max_video_height, cs_self->video_bitrate) ) ? cs_VideoEncoding : 0;
543 cs->capabilities |= ( 0 == init_video_decoder(cs) ) ? cs_VideoDecoding : 0;
544
545 if ( !(cs->capabilities & cs_VideoEncoding) || !(cs->capabilities & cs_VideoDecoding) ) goto error;
546
547 if ( !(cs->frame_buf = calloc(cs->max_video_frame_size, 1)) ) goto error;
548
549 if ( !(cs->split_video_frame = calloc(cs->video_frame_piece_size + VIDEOFRAME_HEADER_SIZE, 1)) )
550 goto error;
551
552 if ( !(cs->vbuf_raw = buffer_new(VIDEO_DECODE_BUFFER_SIZE)) ) goto error;
553 }
554
555 return cs;
556
557error:
558 LOGGER_WARNING("Error initializing codec session! Application might misbehave!");
559
560 pthread_mutex_destroy(cs->queue_mutex);
561
562 if ( cs->audio_encoder ) opus_encoder_destroy(cs->audio_encoder);
563
564 if ( cs->audio_decoder ) opus_decoder_destroy(cs->audio_decoder);
565
566
567 if (has_video) {
568 if ( cs->capabilities & cs_VideoDecoding ) vpx_codec_destroy(&cs->v_decoder);
569
570 if ( cs->capabilities & cs_VideoEncoding ) vpx_codec_destroy(&cs->v_encoder);
571
572 buffer_free(cs->vbuf_raw);
573
574 free(cs->frame_buf);
575 free(cs->split_video_frame);
576 }
577
578 jbuf_free(cs->j_buf);
579 free(cs);
580
581 return NULL;
582}
583
584void cs_kill(CSSession *cs)
585{
586 if (!cs) return;
587
588 /* queue_message will not be called since it's unregistered before cs_kill is called */
589 pthread_mutex_destroy(cs->queue_mutex);
590
591
592 if ( cs->audio_encoder )
593 opus_encoder_destroy(cs->audio_encoder);
594
595 if ( cs->audio_decoder )
596 opus_decoder_destroy(cs->audio_decoder);
597
598 if ( cs->capabilities & cs_VideoDecoding )
599 vpx_codec_destroy(&cs->v_decoder);
600
601 if ( cs->capabilities & cs_VideoEncoding )
602 vpx_codec_destroy(&cs->v_encoder);
603
604 jbuf_free(cs->j_buf);
605 buffer_free(cs->vbuf_raw);
606 free(cs->frame_buf);
607 free(cs->split_video_frame);
608
609 LOGGER_DEBUG("Terminated codec state: %p", cs);
610 free(cs);
611}
612
613
614
615
616/* Called from RTP */
617void queue_message(RTPSession *session, RTPMessage *msg)
618{
619 /* This function is unregistered during call termination befor destroing
620 * Codec session so no need to check for validity of cs
621 */
622 CSSession *cs = session->cs;
623
624 if (!cs) return;
625
626 /* Audio */
627 if (session->payload_type == msi_TypeAudio % 128) {
628 pthread_mutex_lock(cs->queue_mutex);
629 int ret = jbuf_write(cs->j_buf, msg);
630 pthread_mutex_unlock(cs->queue_mutex);
631
632 if (ret == -1) {
633 rtp_free_msg(NULL, msg);
634 }
635 }
636 /* Video */
637 else {
638 uint8_t *packet = msg->data;
639 uint32_t packet_size = msg->length;
640
641 if (packet_size < VIDEOFRAME_HEADER_SIZE)
642 goto end;
643
644 uint8_t diff = packet[0] - cs->frameid_in;
645
646 if (diff != 0) {
647 if (diff < 225) { /* New frame */
648 /* Flush last frames' data and get ready for this frame */
649 Payload *p = malloc(sizeof(Payload) + cs->frame_size);
650
651 if (p) {
652 pthread_mutex_lock(cs->queue_mutex);
653
654 if (buffer_full(cs->vbuf_raw)) {
655 LOGGER_DEBUG("Dropped video frame");
656 Payload *tp;
657 buffer_read(cs->vbuf_raw, &tp);
658 free(tp);
659 } else {
660 p->size = cs->frame_size;
661 memcpy(p->data, cs->frame_buf, cs->frame_size);
662 }
663
664 buffer_write(cs->vbuf_raw, p);
665 pthread_mutex_unlock(cs->queue_mutex);
666 } else {
667 LOGGER_WARNING("Allocation failed! Program might misbehave!");
668 goto end;
669 }
670
671 cs->last_timestamp = msg->header->timestamp;
672 cs->frameid_in = packet[0];
673 memset(cs->frame_buf, 0, cs->frame_size);
674 cs->frame_size = 0;
675
676 } else { /* Old frame; drop */
677 LOGGER_DEBUG("Old packet: %u", packet[0]);
678 goto end;
679 }
680 }
681
682 uint8_t piece_number = packet[1];
683
684 uint32_t length_before_piece = ((piece_number - 1) * cs->video_frame_piece_size);
685 uint32_t framebuf_new_length = length_before_piece + (packet_size - VIDEOFRAME_HEADER_SIZE);
686
687 if (framebuf_new_length > cs->max_video_frame_size) {
688 goto end;
689 }
690
691 /* Otherwise it's part of the frame so just process */
692 /* LOGGER_DEBUG("Video Packet: %u %u", packet[0], packet[1]); */
693
694 memcpy(cs->frame_buf + length_before_piece,
695 packet + VIDEOFRAME_HEADER_SIZE,
696 packet_size - VIDEOFRAME_HEADER_SIZE);
697
698 if (framebuf_new_length > cs->frame_size) {
699 cs->frame_size = framebuf_new_length;
700 }
701
702end:
703 rtp_free_msg(NULL, msg);
704 }
705}
diff --git a/toxav/codec.h b/toxav/codec.h
deleted file mode 100644
index 6018e5df..00000000
--- a/toxav/codec.h
+++ /dev/null
@@ -1,176 +0,0 @@
1/** codec.h
2 *
3 * Audio and video codec intitialization, encoding/decoding and playback
4 *
5 * Copyright (C) 2013 Tox project All Rights Reserved.
6 *
7 * This file is part of Tox.
8 *
9 * Tox is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * Tox is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#ifndef _CODEC_H_
25#define _CODEC_H_
26
27#include "toxav.h"
28#include "rtp.h"
29
30#include <stdio.h>
31#include <math.h>
32#include <pthread.h>
33
34#include <vpx/vpx_decoder.h>
35#include <vpx/vpx_encoder.h>
36#include <vpx/vp8dx.h>
37#include <vpx/vp8cx.h>
38#include <vpx/vpx_image.h>
39#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx())
40#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
41
42/* Audio encoding/decoding */
43#include <opus.h>
44
45#define PAIR(TYPE1__, TYPE2__) struct { TYPE1__ first; TYPE2__ second; }
46
47typedef void (*CSAudioCallback) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data);
48typedef void (*CSVideoCallback) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data);
49
50/**
51 * Codec capabilities
52 */
53typedef enum {
54 cs_AudioEncoding = 1 << 0,
55 cs_AudioDecoding = 1 << 1,
56 cs_VideoEncoding = 1 << 2,
57 cs_VideoDecoding = 1 << 3
58} CSCapabilities;
59
60/**
61 * Codec errors.
62 */
63typedef enum {
64 cs_ErrorSettingVideoResolution = -30,
65 cs_ErrorSettingVideoBitrate = -31,
66 cs_ErrorSplittingVideoPayload = -32,
67} CSError;
68
69/**
70 * Codec session - controling codec
71 */
72typedef struct _CSSession {
73
74 /* VIDEO
75 *
76 *
77 */
78 int support_video;
79
80 /* video encoding */
81 vpx_codec_ctx_t v_encoder;
82 uint32_t frame_counter;
83
84 /* video decoding */
85 vpx_codec_ctx_t v_decoder;
86 int max_width;
87 int max_height;
88 unsigned int video_bitrate;
89
90
91 /* Data handling */
92 uint8_t *frame_buf; /* buffer for split video payloads */
93 uint32_t frame_size; /* largest address written to in frame_buf for current input frame*/
94 uint8_t frameid_in, frameid_out; /* id of input and output video frame */
95 uint32_t last_timestamp; /* calculating cycles */
96
97 /* Limits */
98 uint32_t video_frame_piece_size;
99 uint32_t max_video_frame_size;
100
101 /* Reassembling */
102 uint8_t *split_video_frame;
103 const uint8_t *processing_video_frame;
104 uint16_t processing_video_frame_size;
105
106
107
108 /* AUDIO
109 *
110 *
111 */
112
113 /* audio encoding */
114 OpusEncoder *audio_encoder;
115 int audio_encoder_bitrate;
116 int audio_encoder_sample_rate;
117 int audio_encoder_frame_duration;
118 int audio_encoder_channels;
119
120 /* audio decoding */
121 OpusDecoder *audio_decoder;
122 int audio_decoder_bitrate;
123 int audio_decoder_sample_rate;
124 int audio_decoder_frame_duration;
125 int audio_decoder_channels;
126
127 struct _JitterBuffer *j_buf;
128
129
130 /* Voice activity detection */
131 uint32_t EVAD_tolerance; /* In frames */
132 uint32_t EVAD_tolerance_cr;
133
134
135
136 /* OTHER
137 *
138 *
139 */
140
141 uint64_t capabilities; /* supports*/
142
143 /* Callbacks */
144 PAIR(CSAudioCallback, void *) acb;
145 PAIR(CSVideoCallback, void *) vcb;
146
147 /* Buffering */
148 void *vbuf_raw; /* Un-decoded data */
149 pthread_mutex_t queue_mutex[1];
150
151 void *agent; /* Pointer to ToxAv */
152 int32_t call_idx;
153} CSSession;
154
155/* Make sure to be called BEFORE corresponding rtp_new */
156CSSession *cs_new(const ToxAvCSettings *cs_self, const ToxAvCSettings *cs_peer, uint32_t jbuf_size, int has_video);
157/* Make sure to be called AFTER corresponding rtp_kill */
158void cs_kill(CSSession *cs);
159
160int cs_split_video_payload(CSSession *cs, const uint8_t *payload, uint16_t length);
161const uint8_t *cs_get_split_video_frame(CSSession *cs, uint16_t *size);
162
163/**
164 * Call playback callbacks
165 */
166void cs_do(CSSession *cs);
167
168
169/* Reconfigure video encoder; return 0 on success or -1 on failure. */
170int cs_set_video_encoder_resolution(CSSession *cs, uint16_t width, uint16_t height);
171int cs_set_video_encoder_bitrate(CSSession *cs, uint32_t video_bitrate);
172
173
174/* Internal. Called from rtp_handle_message */
175void queue_message(RTPSession *session, RTPMessage *msg);
176#endif /* _CODEC_H_ */
diff --git a/toxav/group.c b/toxav/group.c
index 817ee6e6..190c2c3d 100644
--- a/toxav/group.c
+++ b/toxav/group.c
@@ -20,7 +20,7 @@
20 20
21#ifdef HAVE_CONFIG_H 21#ifdef HAVE_CONFIG_H
22#include "config.h" 22#include "config.h"
23#endif 23#endif /* HAVE_CONFIG_H */
24 24
25#include "group.h" 25#include "group.h"
26#include "../toxcore/util.h" 26#include "../toxcore/util.h"
@@ -54,7 +54,7 @@ static Group_JitterBuffer *create_queue(unsigned int capacity)
54 54
55 Group_JitterBuffer *q; 55 Group_JitterBuffer *q;
56 56
57 if ( !(q = calloc(sizeof(Group_JitterBuffer), 1)) ) return NULL; 57 if (!(q = calloc(sizeof(Group_JitterBuffer), 1))) return NULL;
58 58
59 if (!(q->queue = calloc(sizeof(Group_Audio_Packet *), size))) { 59 if (!(q->queue = calloc(sizeof(Group_Audio_Packet *), size))) {
60 free(q); 60 free(q);
@@ -190,7 +190,7 @@ static int recreate_encoder(Group_AV *group_av)
190 group_av->audio_encoder = opus_encoder_create(group_av->audio_sample_rate, group_av->audio_channels, 190 group_av->audio_encoder = opus_encoder_create(group_av->audio_sample_rate, group_av->audio_channels,
191 OPUS_APPLICATION_AUDIO, &rc); 191 OPUS_APPLICATION_AUDIO, &rc);
192 192
193 if ( rc != OPUS_OK ) { 193 if (rc != OPUS_OK) {
194 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc)); 194 LOGGER_ERROR("Error while starting audio encoder: %s", opus_strerror(rc));
195 group_av->audio_encoder = NULL; 195 group_av->audio_encoder = NULL;
196 return -1; 196 return -1;
@@ -198,7 +198,7 @@ static int recreate_encoder(Group_AV *group_av)
198 198
199 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_BITRATE(group_av->audio_bitrate)); 199 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_BITRATE(group_av->audio_bitrate));
200 200
201 if ( rc != OPUS_OK ) { 201 if (rc != OPUS_OK) {
202 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); 202 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
203 opus_encoder_destroy(group_av->audio_encoder); 203 opus_encoder_destroy(group_av->audio_encoder);
204 group_av->audio_encoder = NULL; 204 group_av->audio_encoder = NULL;
@@ -207,7 +207,7 @@ static int recreate_encoder(Group_AV *group_av)
207 207
208 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_COMPLEXITY(10)); 208 rc = opus_encoder_ctl(group_av->audio_encoder, OPUS_SET_COMPLEXITY(10));
209 209
210 if ( rc != OPUS_OK ) { 210 if (rc != OPUS_OK) {
211 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc)); 211 LOGGER_ERROR("Error while setting encoder ctl: %s", opus_strerror(rc));
212 opus_encoder_destroy(group_av->audio_encoder); 212 opus_encoder_destroy(group_av->audio_encoder);
213 group_av->audio_encoder = NULL; 213 group_av->audio_encoder = NULL;
@@ -306,7 +306,7 @@ static int decode_audio_packet(Group_AV *group_av, Group_Peer_AV *peer_av, int g
306 int rc; 306 int rc;
307 peer_av->audio_decoder = opus_decoder_create(sample_rate, channels, &rc); 307 peer_av->audio_decoder = opus_decoder_create(sample_rate, channels, &rc);
308 308
309 if ( rc != OPUS_OK ) { 309 if (rc != OPUS_OK) {
310 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc)); 310 LOGGER_ERROR("Error while starting audio decoder: %s", opus_strerror(rc));
311 free(pk); 311 free(pk);
312 return -1; 312 return -1;
diff --git a/toxav/msi.c b/toxav/msi.c
index 97ba936c..7ad39a54 100644
--- a/toxav/msi.c
+++ b/toxav/msi.c
@@ -1,6 +1,6 @@
1/** msi.c 1/** msi.c
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -32,1581 +32,794 @@
32#include <string.h> 32#include <string.h>
33#include <stdlib.h> 33#include <stdlib.h>
34#include <stdbool.h> 34#include <stdbool.h>
35#include <assert.h>
35 36
36#define MSI_MAXMSG_SIZE 256 37#define MSI_MAXMSG_SIZE 256
37 38
38/* Define default timeout for a request.
39 * There is no behavior specified by the msi on what will
40 * client do on timeout, but to call timeout callback.
41 */
42#define m_deftout 10000 /* in milliseconds */
43
44/** 39/**
45 * Protocol: 40 * Protocol:
46 * 41 *
47 * |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}| 42 * |id [1 byte]| |size [1 byte]| |data [$size bytes]| |...{repeat}| |0 {end byte}|
48 */ 43 */
49 44
50typedef uint8_t MSIRawCSettingsType[23];
51
52typedef enum { 45typedef enum {
53 IDRequest = 1, 46 IDRequest = 1,
54 IDResponse, 47 IDError,
55 IDReason, 48 IDCapabilities,
56 IDCallId,
57 IDCSettings,
58 49
59} MSIHeaderID; 50} MSIHeaderID;
60 51
61typedef enum {
62 TypeRequest,
63 TypeResponse,
64
65} MSIMessageType;
66 52
67typedef enum { 53typedef enum {
68 invite, 54 requ_init,
69 start, 55 requ_push,
70 cancel, 56 requ_pop,
71 reject,
72 end,
73
74} MSIRequest; 57} MSIRequest;
75 58
76typedef enum {
77 ringing,
78 starting,
79 ending,
80 error
81
82} MSIResponse;
83
84 59
85#define GENERIC_HEADER(header, val_type) \ 60#define GENERIC_HEADER(header, val_type) \
86typedef struct _MSIHeader##header { \ 61typedef struct { \
87val_type value; \ 62 val_type value; \
88_Bool exists; \ 63 bool exists; \
89} MSIHeader##header; 64} MSIHeader##header
90
91 65
92GENERIC_HEADER ( Request, MSIRequest )
93GENERIC_HEADER ( Response, MSIResponse )
94GENERIC_HEADER ( CallId, MSICallIDType )
95GENERIC_HEADER ( Reason, MSIReasonStrType )
96GENERIC_HEADER ( CSettings, MSIRawCSettingsType )
97 66
67GENERIC_HEADER (Request, MSIRequest);
68GENERIC_HEADER (Error, MSIError);
69GENERIC_HEADER (Capabilities, uint8_t);
98 70
99typedef struct _MSIMessage {
100
101 MSIHeaderRequest request;
102 MSIHeaderResponse response;
103 MSIHeaderReason reason;
104 MSIHeaderCallId callid;
105 MSIHeaderCSettings csettings;
106
107 int friend_id;
108 71
72typedef struct {
73 MSIHeaderRequest request;
74 MSIHeaderError error;
75 MSIHeaderCapabilities capabilities;
109} MSIMessage; 76} MSIMessage;
110 77
111 78
112static void invoke_callback(MSISession *s, int32_t c, MSICallbackID i) 79void msg_init (MSIMessage *dest, MSIRequest request);
113{ 80int msg_parse_in (MSIMessage *dest, const uint8_t *data, uint16_t length);
114 if ( s->callbacks[i].first ) { 81uint8_t *msg_parse_header_out (MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length);
115 LOGGER_DEBUG("Invoking callback function: %d", i); 82static int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg);
83int send_error (Messenger *m, uint32_t friend_number, MSIError error);
84static int invoke_callback(MSICall *call, MSICallbackID cb);
85static MSICall *get_call (MSISession *session, uint32_t friend_number);
86MSICall *new_call (MSISession *session, uint32_t friend_number);
87void kill_call (MSICall *call);
88void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data);
89void handle_init (MSICall *call, const MSIMessage *msg);
90void handle_push (MSICall *call, const MSIMessage *msg);
91void handle_pop (MSICall *call, const MSIMessage *msg);
92void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object);
116 93
117 s->callbacks[i].first( s->agent_handler, c, s->callbacks[i].second );
118 }
119}
120 94
121/** 95/**
122 * Parse raw 'data' received from socket into MSIMessage struct. 96 * Public functions
123 * Every message has to have end value of 'end_byte' or _undefined_ behavior
124 * occures. The best practice is to check the end of the message at the handle_packet.
125 */ 97 */
126static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length ) 98void msi_register_callback (MSISession *session, msi_action_cb *callback, MSICallbackID id)
127{ 99{
128 100 if (!session)
129#define FAIL_CONSTRAINT(constraint, wanted) if ((constraint -= wanted) < 1) { LOGGER_ERROR("Read over length!"); return -1; } 101 return;
130#define FAIL_SIZE(byte, valid) if ( byte != valid ) { LOGGER_ERROR("Invalid data size!"); return -1; } 102
131#define FAIL_LIMITS(byte, high) if ( byte > high ) { LOGGER_ERROR("Failed limit!"); return -1; } 103 pthread_mutex_lock(session->mutex);
132 104 session->callbacks[id] = callback;
133 if ( msg == NULL ) { 105 pthread_mutex_unlock(session->mutex);
134 LOGGER_ERROR("Could not parse message: no storage!");
135 return -1;
136 }
137
138 if ( data[length - 1] ) { /* End byte must have value 0 */
139 LOGGER_ERROR("Invalid end byte");
140 return -1;
141 }
142
143 const uint8_t *it = data;
144 int size_constraint = length;
145
146 while ( *it ) {/* until end byte is hit */
147 switch (*it) {
148 case IDRequest:
149 FAIL_CONSTRAINT(size_constraint, 3);
150 FAIL_SIZE(it[1], 1);
151// FAIL_LIMITS(it[2], invite, end);
152 FAIL_LIMITS(it[2], end);
153 msg->request.value = it[2];
154 it += 3;
155 msg->request.exists = 1;
156 break;
157
158 case IDResponse:
159 FAIL_CONSTRAINT(size_constraint, 3);
160 FAIL_SIZE(it[1], 1);
161// FAIL_LIMITS(it[2], ringing, error);
162 FAIL_LIMITS(it[2], error);
163 msg->response.value = it[2];
164 it += 3;
165 msg->response.exists = 1;
166 break;
167
168 case IDCallId:
169 FAIL_CONSTRAINT(size_constraint, sizeof(MSICallIDType) + 2);
170 FAIL_SIZE(it[1], sizeof(MSICallIDType));
171 memcpy(msg->callid.value, it + 2, sizeof(MSICallIDType));
172 it += sizeof(MSICallIDType) + 2;
173 msg->callid.exists = 1;
174 break;
175
176 case IDReason:
177 FAIL_CONSTRAINT(size_constraint, sizeof(MSIReasonStrType) + 2);
178 FAIL_SIZE(it[1], sizeof(MSIReasonStrType));
179 memcpy(msg->reason.value, it + 2, sizeof(MSIReasonStrType));
180 it += sizeof(MSIReasonStrType) + 2;
181 msg->reason.exists = 1;
182 break;
183
184 case IDCSettings:
185 FAIL_CONSTRAINT(size_constraint, sizeof(MSIRawCSettingsType) + 2);
186 FAIL_SIZE(it[1], sizeof(MSIRawCSettingsType));
187 memcpy(msg->csettings.value, it + 2, sizeof(MSIRawCSettingsType));
188 it += sizeof(MSIRawCSettingsType) + 2;
189 msg->csettings.exists = 1;
190 break;
191
192 default:
193 LOGGER_ERROR("Invalid id byte");
194 return -1;
195 break;
196 }
197 }
198
199 return 0;
200} 106}
201 107MSISession *msi_new (Messenger *m)
202/**
203 * Create the message.
204 */
205MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value )
206{ 108{
207 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); 109 if (m == NULL) {
208 110 LOGGER_ERROR("Could not init session on empty messenger!");
209 if ( retu == NULL ) {
210 LOGGER_WARNING("Allocation failed! Program might misbehave!");
211 return NULL; 111 return NULL;
212 } 112 }
213 113
214 if ( type == TypeRequest ) { 114 MSISession *retu = calloc (sizeof (MSISession), 1);
215 retu->request.exists = 1;
216 retu->request.value = type_value;
217
218 } else {
219 retu->response.exists = 1;
220 retu->response.value = type_value;
221 }
222
223 return retu;
224}
225 115
226 116 if (retu == NULL) {
227/** 117 LOGGER_ERROR("Allocation failed! Program might misbehave!");
228 * Parse data from handle_packet.
229 */
230MSIMessage *parse_recv ( const uint8_t *data, uint16_t length )
231{
232 if ( data == NULL ) {
233 LOGGER_WARNING("Tried to parse empty message!");
234 return NULL; 118 return NULL;
235 } 119 }
236 120
237 MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); 121 if (create_recursive_mutex(retu->mutex) != 0) {
238 122 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
239 if ( retu == NULL ) { 123 free(retu);
240 LOGGER_WARNING("Allocation failed! Program might misbehave!");
241 return NULL; 124 return NULL;
242 } 125 }
243 126
244 if ( parse_raw_data ( retu, data, length ) == -1 ) { 127 retu->messenger = m;
245 128
246 free ( retu ); 129 m_callback_msi_packet(m, handle_msi_packet, retu);
247 return NULL;
248 }
249 130
131 /* This is called when remote terminates session */
132 m_callback_connectionstatus_internal_av(m, on_peer_status, retu);
133
134 LOGGER_DEBUG("New msi session: %p ", retu);
250 return retu; 135 return retu;
251} 136}
252 137int msi_kill (MSISession *session)
253
254/**
255 * Speaks for itself.
256 */
257uint8_t *format_output ( uint8_t *dest,
258 MSIHeaderID id,
259 const void *value,
260 uint8_t value_len,
261 uint16_t *length )
262{ 138{
263 if ( dest == NULL ) { 139 if (session == NULL) {
264 LOGGER_ERROR("No destination space!"); 140 LOGGER_ERROR("Tried to terminate non-existing session");
265 return NULL; 141 return -1;
266 } 142 }
267 143
268 if (value == NULL || value_len == 0) { 144 m_callback_msi_packet((struct Messenger *) session->messenger, NULL, NULL);
269 LOGGER_ERROR("Empty header value"); 145
270 return NULL; 146 if (pthread_mutex_trylock(session->mutex) != 0) {
147 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
148 return -1;
271 } 149 }
272 150
273 *dest = id; 151 if (session->calls) {
274 dest ++; 152 MSIMessage msg;
275 *dest = value_len; 153 msg_init(&msg, requ_pop);
276 dest ++;
277
278 memcpy(dest, value, value_len);
279
280 *length += (2 + value_len);
281
282 return dest + value_len; /* Set to next position ready to be written */
283}
284 154
155 MSICall *it = get_call(session, session->calls_head);
285 156
286/** 157 for (; it; it = it->next) {
287 * Parse MSIMessage to send. 158 send_message(session->messenger, it->friend_number, &msg);
288 */ 159 kill_call(it); /* This will eventually free session->calls */
289uint16_t parse_send ( MSIMessage *msg, uint8_t *dest ) 160 }
290{
291 if (msg == NULL) {
292 LOGGER_ERROR("No message!");
293 return 0;
294 }
295
296 if (dest == NULL ) {
297 LOGGER_ERROR("No destination!");
298 return 0;
299 } 161 }
300 162
301 uint8_t *it = dest; 163 pthread_mutex_unlock(session->mutex);
302 uint16_t size = 0; 164 pthread_mutex_destroy(session->mutex);
303 165
304 if (msg->request.exists) { 166 LOGGER_DEBUG("Terminated session: %p", session);
305 uint8_t cast = msg->request.value; 167 free (session);
306 it = format_output(it, IDRequest, &cast, 1, &size); 168 return 0;
307 } 169}
170int msi_invite (MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities)
171{
172 if (!session)
173 return -1;
174
175 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_number);
308 176
309 if (msg->response.exists) { 177 if (pthread_mutex_trylock(session->mutex) != 0) {
310 uint8_t cast = msg->response.value; 178 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
311 it = format_output(it, IDResponse, &cast, 1, &size); 179 return -1;
312 } 180 }
313 181
314 if (msg->callid.exists) { 182 if (get_call(session, friend_number) != NULL) {
315 it = format_output(it, IDCallId, &msg->callid.value, sizeof(msg->callid.value), &size); 183 LOGGER_ERROR("Already in a call");
184 pthread_mutex_unlock(session->mutex);
185 return -1;
316 } 186 }
317 187
318 if (msg->reason.exists) { 188 (*call) = new_call (session, friend_number);
319 it = format_output(it, IDReason, &msg->reason.value, sizeof(msg->reason.value), &size);
320 }
321 189
322 if (msg->csettings.exists) { 190 if (*call == NULL) {
323 it = format_output(it, IDCSettings, &msg->csettings.value, sizeof(msg->csettings.value), &size); 191 pthread_mutex_unlock(session->mutex);
192 return -1;
324 } 193 }
325 194
326 *it = 0; 195 (*call)->self_capabilities = capabilities;
327 size ++;
328
329 return size;
330}
331
332void msi_msg_set_reason ( MSIMessage *msg, const MSIReasonStrType value )
333{
334 if ( !msg ) return;
335
336 msg->reason.exists = 1;
337 memcpy(msg->reason.value, value, sizeof(MSIReasonStrType));
338}
339
340void msi_msg_set_callid ( MSIMessage *msg, const MSICallIDType value )
341{
342 if ( !msg ) return;
343
344 msg->callid.exists = 1;
345 memcpy(msg->callid.value, value, sizeof(MSICallIDType));
346}
347
348void msi_msg_set_csettings ( MSIMessage *msg, const MSICSettings *value )
349{
350 if ( !msg ) return;
351
352 msg->csettings.exists = 1;
353
354 msg->csettings.value[0] = value->call_type;
355 uint8_t *iter = msg->csettings.value + 1;
356
357 /* Video bitrate */
358 uint32_t lval = htonl(value->video_bitrate);
359 memcpy(iter, &lval, 4);
360 iter += 4;
361
362 /* Video max width */
363 uint16_t sval = htons(value->max_video_width);
364 memcpy(iter, &sval, 2);
365 iter += 2;
366
367 /* Video max height */
368 sval = htons(value->max_video_height);
369 memcpy(iter, &sval, 2);
370 iter += 2;
371
372 /* Audio bitrate */
373 lval = htonl(value->audio_bitrate);
374 memcpy(iter, &lval, 4);
375 iter += 4;
376
377 /* Audio frame duration */
378 sval = htons(value->audio_frame_duration);
379 memcpy(iter, &sval, 2);
380 iter += 2;
381
382 /* Audio sample rate */
383 lval = htonl(value->audio_sample_rate);
384 memcpy(iter, &lval, 4);
385 iter += 4;
386
387 /* Audio channels */
388 lval = htonl(value->audio_channels);
389 memcpy(iter, &lval, 4);
390}
391
392void msi_msg_get_csettings ( MSIMessage *msg, MSICSettings *dest )
393{
394 if ( !msg || !dest || !msg->csettings.exists ) return;
395
396 dest->call_type = msg->csettings.value[0];
397 uint8_t *iter = msg->csettings.value + 1;
398
399 memcpy(&dest->video_bitrate, iter, 4);
400 iter += 4;
401 dest->video_bitrate = ntohl(dest->video_bitrate);
402
403 memcpy(&dest->max_video_width, iter, 2);
404 iter += 2;
405 dest->max_video_width = ntohs(dest->max_video_width);
406 196
407 memcpy(&dest->max_video_height, iter, 2); 197 MSIMessage msg;
408 iter += 2; 198 msg_init(&msg, requ_init);
409 dest->max_video_height = ntohs(dest->max_video_height);
410 199
411 memcpy(&dest->audio_bitrate, iter, 4); 200 msg.capabilities.exists = true;
412 iter += 4; 201 msg.capabilities.value = capabilities;
413 dest->audio_bitrate = ntohl(dest->audio_bitrate);
414 202
415 memcpy(&dest->audio_frame_duration, iter, 2); 203 send_message ((*call)->session->messenger, (*call)->friend_number, &msg);
416 iter += 2;
417 dest->audio_frame_duration = ntohs(dest->audio_frame_duration);
418 204
419 memcpy(&dest->audio_sample_rate, iter, 4); 205 (*call)->state = msi_CallRequesting;
420 iter += 4;
421 dest->audio_sample_rate = ntohl(dest->audio_sample_rate);
422 206
423 memcpy(&dest->audio_channels, iter, 4); 207 LOGGER_DEBUG("Invite sent");
424 dest->audio_channels = ntohl(dest->audio_channels); 208 pthread_mutex_unlock(session->mutex);
209 return 0;
425} 210}
426 211int msi_hangup (MSICall *call)
427typedef struct _Timer {
428 void (*func)(struct _Timer *);
429 uint64_t timeout;
430 MSISession *session;
431 int call_idx;
432 int id;
433
434} Timer;
435
436typedef struct _TimerHandler {
437 Timer **timers;
438
439 uint32_t max_capacity;
440 uint32_t size;
441} TimerHandler;
442
443
444static int timer_alloc (MSISession *session , void (*func)(Timer *), int call_idx, uint32_t timeout)
445{ 212{
446 static int timer_id; 213 if (!call || !call->session)
447 TimerHandler *timer_handler = session->timer_handler; 214 return -1;
448 215
449 uint32_t i = 0; 216 LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_number);
450
451 for (; i < timer_handler->max_capacity && timer_handler->timers[i]; i ++);
452 217
453 if (i == timer_handler->max_capacity) { 218 MSISession *session = call->session;
454 LOGGER_WARNING("Maximum capacity reached!"); 219
220 if (pthread_mutex_trylock(session->mutex) != 0) {
221 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
455 return -1; 222 return -1;
456 } 223 }
457 224
458 Timer *timer = timer_handler->timers[i] = calloc(sizeof(Timer), 1); 225 if (call->state == msi_CallInactive) {
459 226 LOGGER_ERROR("Call is in invalid state!");
460 if (timer == NULL) { 227 pthread_mutex_unlock(session->mutex);
461 LOGGER_ERROR("Failed to allocate timer!");
462 return -1; 228 return -1;
463 } 229 }
464 230
465 timer_handler->size ++; 231 MSIMessage msg;
466 232 msg_init(&msg, requ_pop);
467 timer->func = func;
468 timer->session = session;
469 timer->call_idx = call_idx;
470 timer->timeout = timeout + current_time_monotonic(); /* In ms */
471 ++timer_id;
472 timer->id = timer_id;
473
474 /* reorder */
475 if (i) {
476 int64_t j = i - 1;
477 233
478 for (; j >= 0 && timeout < timer_handler->timers[j]->timeout; j--) { 234 send_message (session->messenger, call->friend_number, &msg);
479 Timer *tmp = timer_handler->timers[j];
480 timer_handler->timers[j] = timer;
481 timer_handler->timers[j + 1] = tmp;
482 }
483 }
484 235
485 LOGGER_DEBUG("Allocated timer index: %ull timeout: %ull, current size: %ull", i, timeout, timer_handler->size); 236 kill_call(call);
486 return timer->id; 237 pthread_mutex_unlock(session->mutex);
238 return 0;
487} 239}
488 240int msi_answer (MSICall *call, uint8_t capabilities)
489static int timer_release ( TimerHandler *timers_container, int id)
490{ 241{
491 Timer **timed_events = timers_container->timers; 242 if (!call || !call->session)
492
493 uint32_t i;
494 int rc = -1;
495
496 for (i = 0; i < timers_container->max_capacity; ++i) {
497 if (timed_events[i] && timed_events[i]->id == id) {
498 rc = i;
499 break;
500 }
501 }
502
503 if (rc == -1) {
504 LOGGER_WARNING("No event with id: %d", id);
505 return -1; 243 return -1;
506 }
507 244
508 free(timed_events[rc]); 245 LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_number);
509 246
510 timed_events[rc] = NULL; 247 MSISession *session = call->session;
511 248
512 i = rc + 1; 249 if (pthread_mutex_trylock(session->mutex) != 0) {
513 250 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
514 for (; i < timers_container->max_capacity && timed_events[i]; i ++) { 251 return -1;
515 timed_events[i - 1] = timed_events[i];
516 timed_events[i] = NULL;
517 }
518
519 timers_container->size--;
520
521 LOGGER_DEBUG("Popped id: %d, current size: %ull ", id, timers_container->size);
522 return 0;
523}
524
525/**
526 * Generate _random_ alphanumerical string.
527 */
528static void t_randomstr ( uint8_t *str, uint32_t size )
529{
530 if (str == NULL) {
531 LOGGER_DEBUG("Empty destination!");
532 return;
533 } 252 }
534 253
535 static const uint8_t _bytes[] = 254 if (call->state != msi_CallRequested) {
536 "0123456789" 255 /* Though sending in invalid state will not cause anything wierd
537 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 256 * Its better to not do it like a maniac */
538 "abcdefghijklmnopqrstuvwxyz"; 257 LOGGER_ERROR("Call is in invalid state!");
539 258 pthread_mutex_unlock(session->mutex);
540 uint32_t _it = 0; 259 return -1;
541
542 for ( ; _it < size; _it++ ) {
543 str[_it] = _bytes[ random_int() % 61 ];
544 } 260 }
545}
546 261
547/* TODO: it would be nice to actually have some sane error codes */ 262 call->self_capabilities = capabilities;
548typedef enum {
549 error_none,
550 error_deadcall, /* has call id but it's from old call */
551 error_id_mismatch, /* non-existing call */
552 263
553 error_no_callid, /* not having call id */ 264 MSIMessage msg;
554 error_no_call, /* no call in session */ 265 msg_init(&msg, requ_push);
555 error_no_crypto_key, /* no crypto key */
556 266
557 error_busy 267 msg.capabilities.exists = true;
268 msg.capabilities.value = capabilities;
558 269
559} MSICallError; /* Error codes */ 270 send_message (session->messenger, call->friend_number, &msg);
560 271
272 call->state = msi_CallActive;
273 pthread_mutex_unlock(session->mutex);
561 274
562/** 275 return 0;
563 * Stringify error code.
564 */
565static const uint8_t *stringify_error ( MSICallError error_code )
566{
567 static const uint8_t *strings[] = {
568 ( uint8_t *) "",
569 ( uint8_t *) "Using dead call",
570 ( uint8_t *) "Call id not set to any call",
571 ( uint8_t *) "Call id not available",
572 ( uint8_t *) "No active call in session",
573 ( uint8_t *) "No Crypto-key set",
574 ( uint8_t *) "Callee busy"
575 };
576
577 return strings[error_code];
578} 276}
579 277int msi_change_capabilities(MSICall *call, uint8_t capabilities)
580static int send_message ( MSISession *session, MSICall *call, MSIMessage *msg, uint32_t to )
581{ 278{
582 msi_msg_set_callid ( msg, call->id ); 279 if (!call || !call->session)
280 return -1;
281
282 LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_number);
583 283
584 uint8_t msg_string_final [MSI_MAXMSG_SIZE]; 284 MSISession *session = call->session;
585 uint16_t length = parse_send ( msg, msg_string_final );
586 285
587 if (!length) { 286 if (pthread_mutex_trylock(session->mutex) != 0) {
588 LOGGER_WARNING("Parsing message failed; nothing sent!"); 287 LOGGER_ERROR ("Failed to aquire lock on msi mutex");
589 return -1; 288 return -1;
590 } 289 }
591 290
592 if ( m_msi_packet(session->messenger_handle, to, msg_string_final, length) ) { 291 if (call->state != msi_CallActive) {
593 LOGGER_DEBUG("Sent message"); 292 LOGGER_ERROR("Call is in invalid state!");
594 return 0; 293 pthread_mutex_unlock(session->mutex);
595 }
596
597 return -1;
598}
599
600static int send_reponse ( MSISession *session, MSICall *call, MSIResponse response, uint32_t to )
601{
602 MSIMessage *msg = msi_new_message ( TypeResponse, response );
603 int ret = send_message ( session, call, msg, to );
604 free ( msg );
605 return ret;
606}
607
608static int send_error ( MSISession *session, MSICall *call, MSICallError errid, uint32_t to )
609{
610 if (!call) {
611 LOGGER_WARNING("Cannot handle error on 'null' call");
612 return -1; 294 return -1;
613 } 295 }
614 296
615 LOGGER_DEBUG("Sending error: %d on call: %s", errid, call->id); 297 call->self_capabilities = capabilities;
616 298
617 MSIMessage *msg_error = msi_new_message ( TypeResponse, error ); 299 MSIMessage msg;
300 msg_init(&msg, requ_push);
618 301
619 msi_msg_set_reason ( msg_error, stringify_error(errid) ); 302 msg.capabilities.exists = true;
620 send_message ( session, call, msg_error, to ); 303 msg.capabilities.value = capabilities;
621 free ( msg_error );
622 304
623 return 0; 305 send_message (call->session->messenger, call->friend_number, &msg);
624}
625 306
626/** 307 pthread_mutex_unlock(session->mutex);
627 * Determine 'bigger' call id 308 return 0;
628 */
629static int call_id_bigger( const uint8_t *first, const uint8_t *second)
630{
631 return (memcmp(first, second, sizeof(MSICallIDType)) < 0);
632} 309}
633 310
634 311
635/** 312/**
636 * Set/change peer csettings 313 * Private functions
637 */ 314 */
638static int flush_peer_csettings ( MSICall *call, MSIMessage *msg, int peer_id ) 315void msg_init(MSIMessage *dest, MSIRequest request)
639{ 316{
640 if ( msg->csettings.exists ) { 317 memset(dest, 0, sizeof(*dest));
641 msi_msg_get_csettings(msg, &call->csettings_peer[peer_id]); 318 dest->request.exists = true;
642 319 dest->request.value = request;
643 LOGGER_DEBUG("Peer: %d \n"
644 "Type: %u \n"
645 "Video bitrate: %u \n"
646 "Video height: %u \n"
647 "Video width: %u \n"
648 "Audio bitrate: %u \n"
649 "Audio framedur: %u \n"
650 "Audio sample rate: %u \n"
651 "Audio channels: %u \n", peer_id,
652 call->csettings_peer[peer_id].call_type,
653 call->csettings_peer[peer_id].video_bitrate,
654 call->csettings_peer[peer_id].max_video_height,
655 call->csettings_peer[peer_id].max_video_width,
656 call->csettings_peer[peer_id].audio_bitrate,
657 call->csettings_peer[peer_id].audio_frame_duration,
658 call->csettings_peer[peer_id].audio_sample_rate,
659 call->csettings_peer[peer_id].audio_channels );
660
661 return 0;
662 }
663
664 LOGGER_WARNING("No csettings header!");
665 return -1;
666} 320}
667 321int msg_parse_in (MSIMessage *dest, const uint8_t *data, uint16_t length)
668
669/**
670 * Add peer to peer list.
671 */
672static void add_peer( MSICall *call, int peer_id )
673{ 322{
674 uint32_t *peers = !call->peers ? peers = calloc(sizeof(uint32_t), 1) : 323 /* Parse raw data received from socket into MSIMessage struct */
675 realloc( call->peers, sizeof(uint32_t) * call->peer_count);
676 324
677 if (!peers) { 325#define CHECK_SIZE(bytes, constraint, size) \
678 LOGGER_WARNING("Allocation failed! Program might misbehave!"); 326 if ((constraint -= (2 + size)) < 1) { LOGGER_ERROR("Read over length!"); return -1; } \
679 return; 327 if (bytes[1] != size) { LOGGER_ERROR("Invalid data size!"); return -1; }
680 }
681 328
682 call->peer_count ++; 329#define CHECK_ENUM_HIGH(bytes, enum_high) /* Assumes size == 1 */ \
683 call->peers = peers; 330 if (bytes[2] > enum_high) { LOGGER_ERROR("Failed enum high limit!"); return -1; }
684 call->peers[call->peer_count - 1] = peer_id;
685
686 LOGGER_DEBUG("Added peer: %d", peer_id);
687}
688 331
332#define SET_UINT8(bytes, header) do { \
333 header.value = bytes[2]; \
334 header.exists = true; \
335 bytes += 3; \
336 } while(0)
689 337
690static MSICall *find_call ( MSISession *session, uint8_t *call_id ) 338#define SET_UINT16(bytes, header) do { \
691{ 339 memcpy(&header.value, bytes + 2, 2);\
692 if ( call_id == NULL ) return NULL; 340 header.exists = true; \
341 bytes += 4; \
342 } while(0)
693 343
694 int32_t i = 0;
695 344
696 for (; i < session->max_calls; i ++ ) 345 assert(dest);
697 if ( session->calls[i] && memcmp(session->calls[i]->id, call_id, sizeof(session->calls[i]->id)) == 0 ) {
698 return session->calls[i];
699 }
700 346
701 return NULL; 347 if (length == 0 || data[length - 1]) { /* End byte must have value 0 */
702} 348 LOGGER_ERROR("Invalid end byte");
703 349 return -1;
704static MSICall *init_call ( MSISession *session, int peers, int ringing_timeout )
705{
706
707 if (peers == 0) {
708 LOGGER_ERROR("No peers!");
709 return NULL;
710 } 350 }
711 351
712 int32_t call_idx = 0; 352 memset(dest, 0, sizeof(*dest));
713
714 for (; call_idx < session->max_calls; call_idx ++) {
715 if ( !session->calls[call_idx] ) {
716
717 if (!(session->calls[call_idx] = calloc ( sizeof ( MSICall ), 1 ))) {
718 LOGGER_WARNING("Allocation failed! Program might misbehave!");
719 return NULL;
720 }
721 353
722 break; 354 const uint8_t *it = data;
723 } 355 int size_constraint = length;
724 }
725
726 if ( call_idx == session->max_calls ) {
727 LOGGER_WARNING("Reached maximum amount of calls!");
728 return NULL;
729 }
730 356
357 while (*it) {/* until end byte is hit */
358 switch (*it) {
359 case IDRequest:
360 CHECK_SIZE(it, size_constraint, 1);
361 CHECK_ENUM_HIGH(it, requ_pop);
362 SET_UINT8(it, dest->request);
363 break;
731 364
732 MSICall *call = session->calls[call_idx]; 365 case IDError:
366 CHECK_SIZE(it, size_constraint, 1);
367 CHECK_ENUM_HIGH(it, msi_EUndisclosed);
368 SET_UINT8(it, dest->error);
369 break;
733 370
734 call->call_idx = call_idx; 371 case IDCapabilities:
372 CHECK_SIZE(it, size_constraint, 1);
373 SET_UINT8(it, dest->capabilities);
374 break;
735 375
736 if ( !(call->csettings_peer = calloc ( sizeof ( MSICSettings ), peers )) ) { 376 default:
737 LOGGER_WARNING("Allocation failed! Program might misbehave!"); 377 LOGGER_ERROR("Invalid id byte");
738 free(call); 378 return -1;
739 return NULL; 379 break;
380 }
740 } 381 }
741 382
742 call->session = session; 383 if (dest->request.exists == false) {
743 384 LOGGER_ERROR("Invalid request field!");
744 call->request_timer_id = 0;
745 call->ringing_timer_id = 0;
746
747 call->ringing_tout_ms = ringing_timeout;
748
749 LOGGER_DEBUG("Started new call with index: %u", call_idx);
750 return call;
751}
752
753static int terminate_call ( MSISession *session, MSICall *call )
754{
755 if ( !call ) {
756 LOGGER_WARNING("Tried to terminate non-existing call!");
757 return -1; 385 return -1;
758 } 386 }
759 387
760 /* Check event loop and cancel timed events if there are any
761 */
762 timer_release ( session->timer_handler, call->request_timer_id);
763 timer_release ( session->timer_handler, call->ringing_timer_id);
764
765 session->calls[call->call_idx] = NULL;
766
767 LOGGER_DEBUG("Terminated call id: %d", call->call_idx);
768
769 free ( call->csettings_peer );
770 free ( call->peers );
771 free ( call );
772
773 return 0; 388 return 0;
774}
775
776static void handle_remote_connection_change(Messenger *messenger, uint32_t friend_num, uint8_t status, void *session_p)
777{
778 (void)messenger;
779 MSISession *session = session_p;
780 389
781 switch ( status ) { 390#undef CHECK_SIZE
782 case 0: { /* Went offline */ 391#undef CHECK_ENUM_HIGH
783 int32_t j = 0; 392#undef SET_UINT8
784 393#undef SET_UINT16
785 for ( ; j < session->max_calls; j ++ ) {
786
787 if ( !session->calls[j] ) continue;
788
789 uint16_t i = 0;
790
791 for ( ; i < session->calls[j]->peer_count; i ++ )
792 if ( session->calls[j]->peers[i] == (uint32_t)friend_num ) {
793 invoke_callback(session, j, msi_OnPeerTimeout);
794 terminate_call(session, session->calls[j]);
795 LOGGER_DEBUG("Remote: %d timed out!", friend_num);
796 return; /* TODO: On group calls change behaviour */
797 }
798 }
799 }
800 break;
801
802 default:
803 break;
804 }
805} 394}
806 395uint8_t *msg_parse_header_out (MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length)
807/**
808 * Function called at request timeout
809 */
810static void handle_timeout ( Timer *timer )
811{ 396{
812 /* TODO: Cancel might not arrive there; set up 397 /* Parse a single header for sending */
813 * timers on these cancels and terminate call on 398 assert(dest);
814 * their timeout 399 assert(value);
815 */ 400 assert(value_len);
816 MSICall *call = timer->session->calls[timer->call_idx];
817
818 401
819 if (call) { 402 *dest = id;
820 LOGGER_DEBUG("[Call: %d] Request timed out!", call->call_idx); 403 dest ++;
404 *dest = value_len;
405 dest ++;
821 406
822 invoke_callback(timer->session, timer->call_idx, msi_OnRequestTimeout); 407 memcpy(dest, value, value_len);
823 msi_cancel(timer->session, timer->call_idx, call->peers [0], "Request timed out");
824 }
825}
826 408
409 *length += (2 + value_len);
827 410
828/********** Request handlers **********/ 411 return dest + value_len; /* Set to next position ready to be written */
829static int handle_recv_invite ( MSISession *session, MSICall *call, MSIMessage *msg ) 412}
413int send_message (Messenger *m, uint32_t friend_number, const MSIMessage *msg)
830{ 414{
831 LOGGER_DEBUG("Session: %p Handling 'invite' on call: %d", session, call ? call->call_idx : -1); 415 /* Parse and send message */
416 assert(m);
832 417
418 uint8_t parsed [MSI_MAXMSG_SIZE];
833 419
834 if (!msg->csettings.exists) {/**/ 420 uint8_t *it = parsed;
835 LOGGER_WARNING("Peer sent invalid codec settings!"); 421 uint16_t size = 0;
836 send_error ( session, call, error_no_callid, msg->friend_id );
837 return 0;
838 }
839 422
840 if ( call ) { 423 if (msg->request.exists) {
841 if ( call->peers[0] == (uint32_t)msg->friend_id ) { 424 uint8_t cast = msg->request.value;
842 if (call->state == msi_CallInviting) { 425 it = msg_parse_header_out(IDRequest, it, &cast,
843 /* The glare case. A calls B when at the same time 426 sizeof(cast), &size);
844 * B calls A. Who has advantage is set bey calculating
845 * 'bigger' Call id and then that call id is being used in
846 * future. User with 'bigger' Call id has the advantage
847 * as in he will wait the response from the other.
848 */
849 LOGGER_DEBUG("Glare case; Peer: %d", call->peers[0]);
850
851 if ( call_id_bigger (call->id, msg->callid.value) == 1 ) { /* Peer has advantage */
852
853 /* Terminate call; peer will timeout(call) if call initialization fails */
854 terminate_call(session, call);
855
856 call = init_call ( session, 1, 0 );
857
858 if ( !call ) {
859 LOGGER_ERROR("Starting call");
860 return 0;
861 }
862
863 } else {
864 return 0; /* Wait for ringing from peer */
865 }
866 } else if (call->state == msi_CallActive) {
867 /* Request for media change; call callback and send starting response */
868 if (flush_peer_csettings(call, msg, 0) != 0) { /**/
869 LOGGER_WARNING("Peer sent invalid csetting!");
870 send_error ( session, call, error_no_callid, msg->friend_id );
871 return 0;
872 }
873
874 LOGGER_DEBUG("Set new call type: %s", call->csettings_peer[0].call_type == msi_TypeAudio ? "audio" : "video");
875 send_reponse(session, call, starting, msg->friend_id);
876 invoke_callback(session, call->call_idx, msi_OnPeerCSChange);
877 return 1;
878 }
879 } else {
880 send_error ( session, call, error_busy, msg->friend_id ); /* TODO: Ugh*/
881 terminate_call(session, call);
882 return 0;
883 }
884 } else { 427 } else {
885 call = init_call ( session, 1, 0 ); 428 LOGGER_DEBUG("Must have request field");
886 429 return -1;
887 if ( !call ) {
888 LOGGER_ERROR("Starting call");
889 return 0;
890 }
891 } 430 }
892 431
893 if ( !msg->callid.exists ) { 432 if (msg->error.exists) {
894 send_error ( session, call, error_no_callid, msg->friend_id ); 433 uint8_t cast = msg->error.value;
895 terminate_call(session, call); 434 it = msg_parse_header_out(IDError, it, &cast,
896 return 0; 435 sizeof(cast), &size);
897 } 436 }
898 437
899 memcpy ( call->id, msg->callid.value, sizeof(msg->callid.value) ); 438 if (msg->capabilities.exists) {
900 call->state = msi_CallStarting; 439 it = msg_parse_header_out(IDCapabilities, it, &msg->capabilities.value,
901 440 sizeof(msg->capabilities.value), &size);
902 add_peer( call, msg->friend_id);
903 flush_peer_csettings ( call, msg, 0 );
904 send_reponse(session, call, ringing, msg->friend_id);
905 invoke_callback(session, call->call_idx, msi_OnInvite);
906
907 return 1;
908}
909
910static int handle_recv_start ( MSISession *session, MSICall *call, MSIMessage *msg )
911{
912 if ( !call ) {
913 LOGGER_WARNING("Session: %p Handling 'start' on no call");
914 return 0;
915 } 441 }
916 442
917 (void)msg; 443 if (it == parsed) {
918 444 LOGGER_WARNING("Parsing message failed; empty message");
919 LOGGER_DEBUG("Session: %p Handling 'start' on call: %d, friend id: %d", session, call->call_idx, msg->friend_id ); 445 return -1;
920
921 call->state = msi_CallActive;
922 invoke_callback(session, call->call_idx, msi_OnStart);
923 return 1;
924}
925
926static int handle_recv_reject ( MSISession *session, MSICall *call, MSIMessage *msg )
927{
928 if ( !call ) {
929 LOGGER_WARNING("Session: %p Handling 'start' on no call");
930 return 0;
931 } 446 }
932 447
933 LOGGER_DEBUG("Session: %p Handling 'reject' on call: %u", session, call->call_idx); 448 *it = 0;
934 449 size ++;
935 invoke_callback(session, call->call_idx, msi_OnReject);
936
937 send_reponse(session, call, ending, msg->friend_id);
938 terminate_call(session, call);
939
940 return 1;
941}
942 450
943static int handle_recv_cancel ( MSISession *session, MSICall *call, MSIMessage *msg ) 451 if (m_msi_packet(m, friend_number, parsed, size)) {
944{ 452 LOGGER_DEBUG("Sent message");
945 if ( !call ) {
946 LOGGER_WARNING("Session: %p Handling 'start' on no call");
947 return 0; 453 return 0;
948 } 454 }
949 455
950 (void)msg; 456 return -1;
951
952 LOGGER_DEBUG("Session: %p Handling 'cancel' on call: %u", session, call->call_idx);
953
954 invoke_callback(session, call->call_idx, msi_OnCancel);
955 terminate_call ( session, call );
956
957 return 1;
958} 457}
959 458int send_error (Messenger *m, uint32_t friend_number, MSIError error)
960static int handle_recv_end ( MSISession *session, MSICall *call, MSIMessage *msg )
961{ 459{
962 if ( !call ) { 460 /* Send error message */
963 LOGGER_WARNING("Session: %p Handling 'start' on no call"); 461 assert(m);
964 return 0;
965 }
966 462
967 LOGGER_DEBUG("Session: %p Handling 'end' on call: %d", session, call->call_idx); 463 LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_number);
968 464
969 invoke_callback(session, call->call_idx, msi_OnEnd); 465 MSIMessage msg;
970 send_reponse(session, call, ending, msg->friend_id); 466 msg_init(&msg, requ_pop);
971 terminate_call ( session, call );
972
973 return 1;
974}
975
976/********** Response handlers **********/
977static int handle_recv_ringing ( MSISession *session, MSICall *call, MSIMessage *msg )
978{
979 if ( !call ) {
980 LOGGER_WARNING("Session: %p Handling 'start' on no call");
981 return 0;
982 }
983 467
984 (void)msg; 468 msg.error.exists = true;
469 msg.error.value = error;
985 470
986 if ( call->ringing_timer_id ) { 471 send_message (m, friend_number, &msg);
987 LOGGER_WARNING("Call already ringing"); 472 return 0;
988 return 0;
989 }
990
991 LOGGER_DEBUG("Session: %p Handling 'ringing' on call: %d", session, call->call_idx );
992
993 call->ringing_timer_id = timer_alloc
994 ( session, handle_timeout, call->call_idx, call->ringing_tout_ms );
995 invoke_callback(session, call->call_idx, msi_OnRinging);
996 return 1;
997} 473}
998static int handle_recv_starting ( MSISession *session, MSICall *call, MSIMessage *msg ) 474int invoke_callback(MSICall *call, MSICallbackID cb)
999{ 475{
1000 if ( !call ) { 476 assert(call);
1001 LOGGER_WARNING("Session: %p Handling 'starting' on non-existing call");
1002 return 0;
1003 }
1004
1005 if ( call->state == msi_CallActive ) { /* Change media */
1006
1007 LOGGER_DEBUG("Session: %p Changing media on call: %d", session, call->call_idx );
1008
1009 invoke_callback(session, call->call_idx, msi_OnSelfCSChange);
1010 477
1011 } else if ( call->state == msi_CallInviting ) { 478 if (call->session->callbacks[cb]) {
1012 LOGGER_DEBUG("Session: %p Handling 'starting' on call: %d", session, call->call_idx ); 479 LOGGER_DEBUG("Invoking callback function: %d", cb);
1013 480
1014 call->state = msi_CallActive; 481 if (call->session->callbacks[cb] (call->session->av, call) != 0) {
1015 482 LOGGER_WARNING("Callback state handling failed, sending error");
1016 MSIMessage *msg_start = msi_new_message ( TypeRequest, start ); 483 goto FAILURE;
1017 send_message ( session, call, msg_start, msg->friend_id ); 484 }
1018 free ( msg_start );
1019
1020
1021 flush_peer_csettings ( call, msg, 0 );
1022
1023 /* This is here in case of glare */
1024 timer_release(session->timer_handler, call->ringing_timer_id);
1025 invoke_callback(session, call->call_idx, msi_OnStart);
1026 } else {
1027 LOGGER_ERROR("Invalid call state");
1028 terminate_call(session, call );
1029 return 0;
1030 }
1031 485
1032 return 1;
1033}
1034static int handle_recv_ending ( MSISession *session, MSICall *call, MSIMessage *msg )
1035{
1036 if ( !call ) {
1037 LOGGER_WARNING("Session: %p Handling 'start' on no call");
1038 return 0; 486 return 0;
1039 } 487 }
1040 488
1041 (void)msg; 489FAILURE:
1042 490 /* If no callback present or error happened while handling,
1043 LOGGER_DEBUG("Session: %p Handling 'ending' on call: %d", session, call->call_idx ); 491 * an error message will be sent to friend
492 */
1044 493
1045 invoke_callback(session, call->call_idx, msi_OnEnd); 494 if (call->error == msi_ENone)
1046 terminate_call ( session, call ); 495 call->error = msi_EHandle;
1047 496
1048 return 1; 497 return -1;
1049} 498}
1050static int handle_recv_error ( MSISession *session, MSICall *call, MSIMessage *msg ) 499static MSICall *get_call (MSISession *session, uint32_t friend_number)
1051{ 500{
1052 if ( !call ) { 501 assert(session);
1053 LOGGER_WARNING("Handling 'error' on non-existing call!");
1054 return -1;
1055 }
1056
1057 LOGGER_DEBUG("Session: %p Handling 'error' on call: %d", session, call->call_idx );
1058
1059 invoke_callback(session, call->call_idx, msi_OnEnd);
1060
1061 /* Handle error accordingly */
1062 if ( msg->reason.exists ) {
1063 /* TODO */
1064 }
1065 502
1066 terminate_call ( session, call ); 503 if (session->calls == NULL || session->calls_tail < friend_number)
504 return NULL;
1067 505
1068 return 1; 506 return session->calls[friend_number];
1069} 507}
1070 508MSICall *new_call (MSISession *session, uint32_t friend_number)
1071/**
1072 * BASIC call flow:
1073 *
1074 * ALICE BOB
1075 * | invite --> |
1076 * | |
1077 * | <-- ringing |
1078 * | |
1079 * | <-- starting |
1080 * | |
1081 * | start --> |
1082 * | |
1083 * | <-- MEDIA TRANS --> |
1084 * | |
1085 * | end --> |
1086 * | |
1087 * | <-- ending |
1088 *
1089 * Alice calls Bob by sending invite packet.
1090 * Bob recvs the packet and sends an ringing packet;
1091 * which notifies Alice that her invite is acknowledged.
1092 * Ringing screen shown on both sides.
1093 * Bob accepts the invite for a call by sending starting packet.
1094 * Alice recvs the starting packet and sends the started packet to
1095 * inform Bob that she recved the starting packet.
1096 * Now the media transmission is established ( i.e. RTP transmission ).
1097 * Alice hangs up and sends end packet.
1098 * Bob recves the end packet and sends ending packet
1099 * as the acknowledgement that the call is ending.
1100 *
1101 *
1102 */
1103static void msi_handle_packet ( Messenger *messenger, uint32_t source, const uint8_t *data, uint16_t length,
1104 void *object )
1105{ 509{
1106 LOGGER_DEBUG("Got msi message"); 510 assert(session);
1107 /* Unused */
1108 (void)messenger;
1109 511
1110 MSISession *session = object; 512 MSICall *rc = calloc(sizeof(MSICall), 1);
1111 MSIMessage *msg;
1112
1113 if ( !length ) {
1114 LOGGER_WARNING("Length param negative");
1115 return;
1116 }
1117
1118 msg = parse_recv ( data, length );
1119
1120 if ( !msg ) {
1121 LOGGER_WARNING("Error parsing message");
1122 return;
1123 } else {
1124 LOGGER_DEBUG("Successfully parsed message");
1125 }
1126
1127 msg->friend_id = source;
1128 513
1129 pthread_mutex_lock(session->mutex); 514 if (rc == NULL)
1130 515 return NULL;
1131 /* Find what call */
1132 MSICall *call = msg->callid.exists ? find_call(session, msg->callid.value ) : NULL;
1133 516
1134 /* Now handle message */ 517 rc->session = session;
518 rc->friend_number = friend_number;
1135 519
1136 if ( msg->request.exists ) { /* Handle request */ 520 if (session->calls == NULL) { /* Creating */
521 session->calls = calloc (sizeof(MSICall *), friend_number + 1);
1137 522
1138 switch (msg->request.value) { 523 if (session->calls == NULL) {
1139 case invite: 524 free(rc);
1140 handle_recv_invite ( session, call, msg ); 525 return NULL;
1141 break; 526 }
1142 527
1143 case start: 528 session->calls_tail = session->calls_head = friend_number;
1144 handle_recv_start ( session, call, msg );
1145 break;
1146 529
1147 case cancel: 530 } else if (session->calls_tail < friend_number) { /* Appending */
1148 handle_recv_cancel ( session, call, msg ); 531 void *tmp = realloc(session->calls, sizeof(MSICall *) * (friend_number + 1));
1149 break;
1150 532
1151 case reject: 533 if (tmp == NULL) {
1152 handle_recv_reject ( session, call, msg ); 534 free(rc);
1153 break; 535 return NULL;
1154
1155 case end:
1156 handle_recv_end ( session, call, msg );
1157 break;
1158 } 536 }
1159 537
1160 } else if ( msg->response.exists ) { /* Handle response */ 538 session->calls = tmp;
1161 539
1162 /* Got response so cancel timer */ 540 /* Set fields in between to null */
1163 if ( call ) timer_release(session->timer_handler, call->request_timer_id); 541 uint32_t i = session->calls_tail + 1;
1164 542
1165 switch (msg->response.value) { 543 for (; i < friend_number; i ++)
1166 case ringing: 544 session->calls[i] = NULL;
1167 handle_recv_ringing ( session, call, msg );
1168 break;
1169
1170 case starting:
1171 handle_recv_starting ( session, call, msg );
1172 break;
1173 545
1174 case ending: 546 rc->prev = session->calls[session->calls_tail];
1175 handle_recv_ending ( session, call, msg ); 547 session->calls[session->calls_tail]->next = rc;
1176 break;
1177 548
1178 case error: 549 session->calls_tail = friend_number;
1179 handle_recv_error ( session, call, msg );
1180 break;
1181 }
1182 550
1183 } else { 551 } else if (session->calls_head > friend_number) { /* Inserting at front */
1184 LOGGER_WARNING("Invalid message: no resp nor requ headers"); 552 rc->next = session->calls[session->calls_head];
553 session->calls[session->calls_head]->prev = rc;
554 session->calls_head = friend_number;
1185 } 555 }
1186 556
1187 free ( msg ); 557 session->calls[friend_number] = rc;
1188 558 return rc;
1189 pthread_mutex_unlock(session->mutex);
1190} 559}
1191 560void kill_call (MSICall *call)
1192
1193
1194/********** User functions **********/
1195void msi_register_callback ( MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata )
1196{ 561{
1197 session->callbacks[id].first = callback; 562 /* Assume that session mutex is locked */
1198 session->callbacks[id].second = userdata; 563 if (call == NULL)
1199} 564 return;
1200
1201
1202MSISession *msi_new ( Messenger *messenger, int32_t max_calls )
1203{
1204 if (messenger == NULL) {
1205 LOGGER_ERROR("Could not init session on empty messenger!");
1206 return NULL;
1207 }
1208
1209 if ( !max_calls ) {
1210 LOGGER_WARNING("Invalid max call treshold!");
1211 return NULL;
1212 }
1213
1214 MSISession *retu = calloc ( sizeof ( MSISession ), 1 );
1215
1216 if (retu == NULL) {
1217 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1218 return NULL;
1219 }
1220
1221 if (!(retu->calls = calloc( sizeof (MSICall *), max_calls ))) {
1222 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1223 goto error;
1224 }
1225
1226 retu->timer_handler = calloc(1, sizeof(TimerHandler));
1227
1228 if (retu->timer_handler == NULL) {
1229 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1230 goto error;
1231 }
1232
1233 /* Allocate space for timers */
1234 ((TimerHandler *)retu->timer_handler)->max_capacity = max_calls * 10;
1235
1236 if (!(((TimerHandler *)retu->timer_handler)->timers = calloc(max_calls * 10, sizeof(Timer *)))) {
1237 LOGGER_ERROR("Allocation failed! Program might misbehave!");
1238 goto error;
1239 }
1240 565
1241 if (create_recursive_mutex(retu->mutex) != 0) { 566 LOGGER_DEBUG("Killing call: %p", call);
1242 LOGGER_ERROR("Failed to init mutex! Program might misbehave");
1243 goto error;
1244 }
1245 567
1246 retu->messenger_handle = messenger; 568 MSISession *session = call->session;
1247 retu->agent_handler = NULL;
1248 retu->max_calls = max_calls;
1249 retu->frequ = 10000; /* default value? */
1250 retu->call_timeout = 30000; /* default value? */
1251 569
1252 m_callback_msi_packet(messenger, msi_handle_packet, retu ); 570 MSICall *prev = call->prev;
571 MSICall *next = call->next;
1253 572
1254 /* This is called when remote terminates session */ 573 if (prev)
1255 m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, retu); 574 prev->next = next;
575 else if (next)
576 session->calls_head = next->friend_number;
577 else goto CLEAR_CONTAINER;
1256 578
1257 LOGGER_DEBUG("New msi session: %p max calls: %u", retu, max_calls); 579 if (next)
1258 return retu; 580 next->prev = prev;
581 else if (prev)
582 session->calls_tail = prev->friend_number;
583 else goto CLEAR_CONTAINER;
1259 584
1260error: 585 session->calls[call->friend_number] = NULL;
586 free(call);
587 return;
1261 588
1262 if (retu->timer_handler) { 589CLEAR_CONTAINER:
1263 free(((TimerHandler *)retu->timer_handler)->timers); 590 session->calls_head = session->calls_tail = 0;
1264 free(retu->timer_handler); 591 free(session->calls);
1265 } 592 free(call);
1266 593 session->calls = NULL;
1267 free(retu->calls);
1268 free(retu);
1269 return NULL;
1270} 594}
1271 595void on_peer_status(Messenger *m, uint32_t friend_number, uint8_t status, void *data)
1272
1273int msi_kill ( MSISession *session )
1274{ 596{
1275 if (session == NULL) { 597 (void)m;
1276 LOGGER_ERROR("Tried to terminate non-existing session"); 598 MSISession *session = data;
1277 return -1;
1278 }
1279 599
1280 m_callback_msi_packet((struct Messenger *) session->messenger_handle, NULL, NULL); 600 switch (status) {
1281 pthread_mutex_lock(session->mutex); 601 case 0: { /* Friend is now offline */
602 LOGGER_DEBUG("Friend %d is now offline", friend_number);
1282 603
1283 /* Cancel active calls */ 604 pthread_mutex_lock(session->mutex);
1284 int32_t idx = 0; 605 MSICall *call = get_call(session, friend_number);
1285
1286 for (; idx < session->max_calls; idx ++) if ( session->calls[idx] ) {
1287 /* Cancel all? */
1288 uint16_t _it = 0;
1289 /*for ( ; _it < session->calls[idx]->peer_count; _it++ )
1290 * FIXME: will not work on multiple peers, must cancel call for all peers
1291 */
1292 MSICallState state = session->calls[idx]->state;
1293
1294 if (state == msi_CallInviting) {
1295 msi_cancel( session, idx, session->calls[idx]->peers [_it], "MSI session terminated!" );
1296 } else {
1297 msi_stopcall(session, idx);
1298 }
1299 }
1300
1301 free(((TimerHandler *)session->timer_handler)->timers);
1302 free(session->timer_handler);
1303
1304 free ( session->calls );
1305 pthread_mutex_unlock(session->mutex);
1306 pthread_mutex_destroy(session->mutex);
1307
1308 LOGGER_DEBUG("Terminated session: %p", session);
1309 free ( session );
1310 return 0;
1311}
1312
1313int msi_invite ( MSISession *session,
1314 int32_t *call_index,
1315 const MSICSettings *csettings,
1316 uint32_t rngsec,
1317 uint32_t friend_id )
1318{
1319 pthread_mutex_lock(session->mutex);
1320
1321 LOGGER_DEBUG("Session: %p Inviting friend: %u", session, friend_id);
1322 606
607 if (call == NULL) {
608 pthread_mutex_unlock(session->mutex);
609 return;
610 }
1323 611
1324 int i = 0; 612 invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */
1325 613 kill_call(call);
1326 for (; i < session->max_calls; i ++)
1327 if (session->calls[i] && session->calls[i]->peers[0] == friend_id) {
1328 LOGGER_ERROR("Already in a call with friend %d", friend_id);
1329 pthread_mutex_unlock(session->mutex); 614 pthread_mutex_unlock(session->mutex);
1330 return msi_ErrorAlreadyInCallWithPeer;
1331 } 615 }
616 break;
1332 617
1333 618 default:
1334 MSICall *call = init_call ( session, 1, rngsec ); /* Just one peer for now */ 619 break;
1335
1336 if ( !call ) {
1337 pthread_mutex_unlock(session->mutex);
1338 LOGGER_ERROR("Cannot handle more calls");
1339 return msi_ErrorReachedCallLimit;
1340 } 620 }
1341
1342 *call_index = call->call_idx;
1343
1344 t_randomstr ( call->id, sizeof(call->id) );
1345
1346 add_peer ( call, friend_id );
1347
1348 call->csettings_local = *csettings;
1349
1350 MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite );
1351
1352 msi_msg_set_csettings(msg_invite, csettings);
1353 send_message ( session, call, msg_invite, friend_id );
1354 free( msg_invite );
1355
1356 call->state = msi_CallInviting;
1357
1358 call->request_timer_id = timer_alloc ( session, handle_timeout, call->call_idx, m_deftout );
1359
1360 LOGGER_DEBUG("Invite sent");
1361
1362 pthread_mutex_unlock(session->mutex);
1363
1364 return 0;
1365} 621}
1366 622void handle_init (MSICall* call, const MSIMessage* msg)
1367int msi_hangup ( MSISession *session, int32_t call_index )
1368{ 623{
1369 pthread_mutex_lock(session->mutex); 624 assert(call);
1370 LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index); 625 LOGGER_DEBUG("Session: %p Handling 'init' friend: %d", call->session, call->friend_number);
1371 626
1372 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 627 if (!msg->capabilities.exists) {
1373 LOGGER_ERROR("Invalid call index!"); 628 LOGGER_WARNING("Session: %p Invalid capabilities on 'init'");
1374 pthread_mutex_unlock(session->mutex); 629 call->error = msi_EInvalidMessage;
1375 return msi_ErrorNoCall; 630 goto FAILURE;
1376 } 631 }
632
633 switch (call->state)
634 {
635 case msi_CallInactive: {
636 /* Call requested */
637 call->peer_capabilities = msg->capabilities.value;
638 call->state = msi_CallRequested;
1377 639
1378 if ( session->calls[call_index]->state != msi_CallActive ) { 640 if (invoke_callback(call, msi_OnInvite) == -1)
1379 LOGGER_ERROR("Call is not active!"); 641 goto FAILURE;
1380 pthread_mutex_unlock(session->mutex); 642 }
1381 return msi_ErrorInvalidState; 643 break;
644
645 case msi_CallActive: {
646 /* If peer sent init while the call is already
647 * active it's probable that he is trying to
648 * re-call us while the call is not terminated
649 * on our side. We can assume that in this case
650 * we can automatically answer the re-call.
651 */
652
653 LOGGER_INFO("Friend is recalling us");
654
655 MSIMessage msg;
656 msg_init(&msg, requ_push);
657
658 msg.capabilities.exists = true;
659 msg.capabilities.value = call->self_capabilities;
660
661 send_message (call->session->messenger, call->friend_number, &msg);
662
663 /* If peer changed capabilities during re-call they will
664 * be handled accordingly during the next step
665 */
666 }
667 break;
668
669 default: {
670 LOGGER_WARNING("Session: %p Invalid state on 'init'");
671 call->error = msi_EInvalidState;
672 goto FAILURE;
673 }
674 break;
1382 } 675 }
1383 676
1384 MSIMessage *msg_end = msi_new_message ( TypeRequest, end ); 677 return;
1385 678FAILURE:
1386 /* hangup for each peer */ 679 send_error(call->session->messenger, call->friend_number, call->error);
1387 int it = 0; 680 kill_call(call);
1388
1389 for ( ; it < session->calls[call_index]->peer_count; it ++ )
1390 send_message ( session, session->calls[call_index], msg_end, session->calls[call_index]->peers[it] );
1391
1392 session->calls[call_index]->state = msi_CallOver;
1393
1394 free ( msg_end );
1395
1396 session->calls[call_index]->request_timer_id =
1397 timer_alloc ( session, handle_timeout, call_index, m_deftout );
1398
1399 pthread_mutex_unlock(session->mutex);
1400 return 0;
1401} 681}
1402 682void handle_push (MSICall *call, const MSIMessage *msg)
1403int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings )
1404{ 683{
1405 pthread_mutex_lock(session->mutex); 684 assert(call);
1406 LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index);
1407 685
1408 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 686 LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_number);
1409 LOGGER_ERROR("Invalid call index!");
1410 pthread_mutex_unlock(session->mutex);
1411 return msi_ErrorNoCall;
1412 }
1413 687
1414 if ( session->calls[call_index]->state != msi_CallStarting ) { 688 if (!msg->capabilities.exists) {
1415 LOGGER_ERROR("Call is in invalid state!"); 689 LOGGER_WARNING("Session: %p Invalid capabilities on 'push'");
1416 pthread_mutex_unlock(session->mutex); 690 call->error = msi_EInvalidMessage;
1417 return msi_ErrorInvalidState; 691 goto FAILURE;
1418 } 692 }
1419 693
1420 MSIMessage *msg_starting = msi_new_message ( TypeResponse, starting ); 694 switch (call->state) {
1421 695 case msi_CallActive: {
1422 session->calls[call_index]->csettings_local = *csettings; 696 /* Only act if capabilities changed */
1423 697 if (call->peer_capabilities != msg->capabilities.value) {
1424 msi_msg_set_csettings(msg_starting, csettings); 698 LOGGER_INFO("Friend is changing capabilities to: %u", msg->capabilities.value);
1425 699
1426 send_message ( session, session->calls[call_index], msg_starting, session->calls[call_index]->peers[0] ); 700 call->peer_capabilities = msg->capabilities.value;
1427 free ( msg_starting );
1428
1429 session->calls[call_index]->state = msi_CallActive;
1430
1431 pthread_mutex_unlock(session->mutex);
1432 return 0;
1433}
1434 701
1435int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ) 702 if (invoke_callback(call, msi_OnCapabilities) == -1)
1436{ 703 goto FAILURE;
1437 pthread_mutex_lock(session->mutex); 704 }
1438 LOGGER_DEBUG("Session: %p Canceling call: %u; reason: %s", session, call_index, reason ? reason : "Unknown"); 705 }
706 break;
1439 707
1440 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 708 case msi_CallRequesting: {
1441 LOGGER_ERROR("Invalid call index!"); 709 LOGGER_INFO("Friend answered our call");
1442 pthread_mutex_unlock(session->mutex);
1443 return msi_ErrorNoCall;
1444 }
1445 710
1446 if ( session->calls[call_index]->state != msi_CallInviting ) { 711 /* Call started */
1447 LOGGER_ERROR("Call is in invalid state: %u", session->calls[call_index]->state); 712 call->peer_capabilities = msg->capabilities.value;
1448 pthread_mutex_unlock(session->mutex); 713 call->state = msi_CallActive;
1449 return msi_ErrorInvalidState;
1450 }
1451 714
1452 MSIMessage *msg_cancel = msi_new_message ( TypeRequest, cancel ); 715 if (invoke_callback(call, msi_OnStart) == -1)
716 goto FAILURE;
1453 717
1454 /* FIXME */ 718 }
1455#if 0 719 break;
1456 720
1457 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) { 721 /* Pushes during initialization state are ignored */
1458 MSIReasonStrType reason_cast; 722 case msi_CallInactive:
1459 memset(reason_cast, '\0', sizeof(MSIReasonStrType)); 723 case msi_CallRequested: {
1460 memcpy(reason_cast, reason, strlen(reason)); 724 LOGGER_WARNING("Ignoring invalid push");
1461 msi_msg_set_reason(msg_cancel, reason_cast); 725 }
726 break;
1462 } 727 }
1463 728
1464#else 729 return;
1465 (void)reason;
1466
1467#endif
1468 730
1469 send_message ( session, session->calls[call_index], msg_cancel, peer ); 731FAILURE:
1470 free ( msg_cancel ); 732 send_error(call->session->messenger, call->friend_number, call->error);
1471 733 kill_call(call);
1472 terminate_call ( session, session->calls[call_index] );
1473 pthread_mutex_unlock(session->mutex);
1474
1475 return 0;
1476} 734}
1477 735void handle_pop (MSICall *call, const MSIMessage *msg)
1478int msi_reject ( MSISession *session, int32_t call_index, const char *reason )
1479{ 736{
1480 pthread_mutex_lock(session->mutex); 737 assert(call);
1481 LOGGER_DEBUG("Session: %p Rejecting call: %u; reason: %s", session, call_index, reason ? reason : "Unknown");
1482
1483 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1484 LOGGER_ERROR("Invalid call index!");
1485 pthread_mutex_unlock(session->mutex);
1486 return msi_ErrorNoCall;
1487 }
1488
1489 if ( session->calls[call_index]->state != msi_CallStarting ) {
1490 LOGGER_ERROR("Call is in invalid state!");
1491 pthread_mutex_unlock(session->mutex);
1492 return msi_ErrorInvalidState;
1493 }
1494
1495 MSIMessage *msg_reject = msi_new_message ( TypeRequest, reject );
1496 738
1497 /* FIXME */ 739 LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_number);
1498#if 0
1499 740
1500 if ( reason && strlen(reason) < sizeof(MSIReasonStrType) ) { 741 /* callback errors are ignored */
1501 MSIReasonStrType reason_cast;
1502 memset(reason_cast, '\0', sizeof(MSIReasonStrType));
1503 memcpy(reason_cast, reason, strlen(reason));
1504 msi_msg_set_reason(msg_reject, reason_cast);
1505 }
1506
1507#else
1508 (void)reason;
1509
1510#endif
1511 742
1512 send_message ( session, session->calls[call_index], msg_reject, 743 if (msg->error.exists) {
1513 session->calls[call_index]->peers[session->calls[call_index]->peer_count - 1] ); 744 LOGGER_WARNING("Friend detected an error: %d", msg->error.value);
1514 free ( msg_reject ); 745 call->error = msg->error.value;
746 invoke_callback(call, msi_OnError);
1515 747
1516 session->calls[call_index]->state = msi_CallOver; 748 } else switch (call->state) {
1517 session->calls[call_index]->request_timer_id = 749 case msi_CallInactive: {
1518 timer_alloc ( session, handle_timeout, call_index, m_deftout ); 750 LOGGER_ERROR("Handling what should be impossible case");
751 abort();
752 }
753 break;
1519 754
1520 pthread_mutex_unlock(session->mutex); 755 case msi_CallActive: {
1521 return 0; 756 /* Hangup */
1522} 757 LOGGER_INFO("Friend hung up on us");
758 invoke_callback(call, msi_OnEnd);
759 }
760 break;
1523 761
1524int msi_stopcall ( MSISession *session, int32_t call_index ) 762 case msi_CallRequesting: {
1525{ 763 /* Reject */
1526 pthread_mutex_lock(session->mutex); 764 LOGGER_INFO("Friend rejected our call");
1527 LOGGER_DEBUG("Session: %p Stopping call index: %u", session, call_index); 765 invoke_callback(call, msi_OnEnd);
766 }
767 break;
1528 768
1529 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) { 769 case msi_CallRequested: {
1530 pthread_mutex_unlock(session->mutex); 770 /* Cancel */
1531 return msi_ErrorNoCall; 771 LOGGER_INFO("Friend canceled call invite");
772 invoke_callback(call, msi_OnEnd);
773 }
774 break;
1532 } 775 }
1533 776
1534 /* just terminate it */ 777 kill_call (call);
1535
1536 terminate_call ( session, session->calls[call_index] );
1537
1538 pthread_mutex_unlock(session->mutex);
1539 return 0;
1540} 778}
1541 779void handle_msi_packet (Messenger *m, uint32_t friend_number, const uint8_t *data, uint16_t length, void *object)
1542int msi_change_csettings(MSISession *session, int32_t call_index, const MSICSettings *csettings)
1543{ 780{
1544 pthread_mutex_lock(session->mutex); 781 LOGGER_DEBUG("Got msi message");
1545
1546 LOGGER_DEBUG("Changing media on call: %d", call_index);
1547
1548 if ( call_index < 0 || call_index >= session->max_calls || !session->calls[call_index] ) {
1549 LOGGER_ERROR("Invalid call index!");
1550 pthread_mutex_unlock(session->mutex);
1551 return msi_ErrorNoCall;
1552 }
1553
1554 MSICall *call = session->calls[call_index];
1555 782
1556 if ( call->state != msi_CallActive ) { 783 MSISession *session = object;
1557 LOGGER_ERROR("Call is not active!"); 784 MSIMessage msg;
1558 pthread_mutex_unlock(session->mutex);
1559 return msi_ErrorInvalidState;
1560 }
1561 785
1562 MSICSettings *local = &call->csettings_local; 786 if (msg_parse_in (&msg, data, length) == -1) {
1563 787 LOGGER_WARNING("Error parsing message");
1564 if ( 788 send_error(m, friend_number, msi_EInvalidMessage);
1565 local->call_type == csettings->call_type && 789 return;
1566 local->video_bitrate == csettings->video_bitrate && 790 } else {
1567 local->max_video_width == csettings->max_video_width && 791 LOGGER_DEBUG("Successfully parsed message");
1568 local->max_video_height == csettings->max_video_height &&
1569 local->audio_bitrate == csettings->audio_bitrate &&
1570 local->audio_frame_duration == csettings->audio_frame_duration &&
1571 local->audio_sample_rate == csettings->audio_sample_rate &&
1572 local->audio_channels == csettings->audio_channels ) {
1573 LOGGER_ERROR("Call is already set accordingly!");
1574 pthread_mutex_unlock(session->mutex);
1575 return -1;
1576 } 792 }
1577 793
1578 *local = *csettings;
1579
1580 MSIMessage *msg_invite = msi_new_message ( TypeRequest, invite );
1581
1582 msi_msg_set_csettings ( msg_invite, local );
1583 send_message ( session, call, msg_invite, call->peers[0] );
1584 free ( msg_invite );
1585
1586 LOGGER_DEBUG("Request for media change sent");
1587
1588 pthread_mutex_unlock(session->mutex);
1589
1590 return 0;
1591}
1592
1593void msi_do(MSISession *session)
1594{
1595 pthread_mutex_lock(session->mutex); 794 pthread_mutex_lock(session->mutex);
795 MSICall *call = get_call(session, friend_number);
1596 796
1597 TimerHandler *timer = session->timer_handler; 797 if (call == NULL) {
1598 798 if (msg.request.value != requ_init) {
1599 uint64_t time = current_time_monotonic(); 799 send_error(m, friend_number, msi_EStrayMessage);
800 pthread_mutex_unlock(session->mutex);
801 return;
802 }
1600 803
1601 while ( timer->timers[0] && timer->timers[0]->timeout < time ) { 804 call = new_call(session, friend_number);
1602 LOGGER_DEBUG("Executing timer assigned at: %d", timer->timers[0]->timeout);
1603 805
1604 int id = timer->timers[0]->id; 806 if (call == NULL) {
1605 timer->timers[0]->func(timer->timers[0]); 807 send_error(m, friend_number, msi_ESystem);
808 pthread_mutex_unlock(session->mutex);
809 return;
810 }
811 }
1606 812
1607 /* In case function has released timer */ 813 switch (msg.request.value) {
1608 if (timer->timers[0] && timer->timers[0]->id == id) 814 case requ_init:
1609 timer_release(timer, id); 815 handle_init(call, &msg);
816 break;
817 case requ_push:
818 handle_push(call, &msg);
819 break;
820 case requ_pop:
821 handle_pop(call, &msg); /* always kills the call */
822 break;
1610 } 823 }
1611 824
1612 pthread_mutex_unlock(session->mutex); 825 pthread_mutex_unlock(session->mutex);
diff --git a/toxav/msi.h b/toxav/msi.h
index 6a00def6..e69581d1 100644
--- a/toxav/msi.h
+++ b/toxav/msi.h
@@ -1,6 +1,6 @@
1/** msi.h 1/** msi.h
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -19,184 +19,133 @@
19 * 19 *
20 */ 20 */
21 21
22#ifndef __TOXMSI 22#ifndef MSI_H
23#define __TOXMSI 23#define MSI_H
24 24
25#include "codec.h" 25#include <inttypes.h>
26#include <pthread.h>
27
28#include "audio.h"
29#include "video.h"
26#include "../toxcore/Messenger.h" 30#include "../toxcore/Messenger.h"
27 31
28typedef uint8_t MSICallIDType[12]; 32/**
29typedef uint8_t MSIReasonStrType[255]; 33 * Error codes.
30typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx, void *arg ); 34 */
35typedef enum {
36 msi_ENone,
37 msi_EInvalidMessage,
38 msi_EInvalidParam,
39 msi_EInvalidState,
40 msi_EStrayMessage,
41 msi_ESystem,
42 msi_EHandle,
43 msi_EUndisclosed, /* NOTE: must be last enum otherwise parsing will not work */
44} MSIError;
31 45
32/** 46/**
33 * Call type identifier. Also used as rtp callback prefix. 47 * Supported capabilities
34 */ 48 */
35typedef enum { 49typedef enum {
36 msi_TypeAudio = 192, 50 msi_CapSAudio = 4, /* sending audio */
37 msi_TypeVideo 51 msi_CapSVideo = 8, /* sending video */
38} MSICallType; 52 msi_CapRAudio = 16, /* receiving audio */
53 msi_CapRVideo = 32, /* receiving video */
54} MSICapabilities;
39 55
40 56
41/** 57/**
42 * Call state identifiers. 58 * Call state identifiers.
43 */ 59 */
44typedef enum { 60typedef enum {
45 msi_CallInviting, /* when sending call invite */ 61 msi_CallInactive, /* Default */
46 msi_CallStarting, /* when getting call invite */
47 msi_CallActive, 62 msi_CallActive,
48 msi_CallHold, 63 msi_CallRequesting, /* when sending call invite */
49 msi_CallOver 64 msi_CallRequested, /* when getting call invite */
50
51} MSICallState; 65} MSICallState;
52 66
53
54/**
55 * Encoding settings.
56 */
57typedef struct _MSICodecSettings {
58 MSICallType call_type;
59
60 uint32_t video_bitrate; /* In kbits/s */
61 uint16_t max_video_width; /* In px */
62 uint16_t max_video_height; /* In px */
63
64 uint32_t audio_bitrate; /* In bits/s */
65 uint16_t audio_frame_duration; /* In ms */
66 uint32_t audio_sample_rate; /* In Hz */
67 uint32_t audio_channels;
68} MSICSettings;
69
70
71/** 67/**
72 * Callbacks ids that handle the states 68 * Callbacks ids that handle the states
73 */ 69 */
74typedef enum { 70typedef enum {
75 msi_OnInvite, /* Incoming call */ 71 msi_OnInvite, /* Incoming call */
76 msi_OnRinging, /* When peer is ready to accept/reject the call */
77 msi_OnStart, /* Call (RTP transmission) started */ 72 msi_OnStart, /* Call (RTP transmission) started */
78 msi_OnCancel, /* The side that initiated call canceled invite */
79 msi_OnReject, /* The side that was invited rejected the call */
80 msi_OnEnd, /* Call that was active ended */ 73 msi_OnEnd, /* Call that was active ended */
81 msi_OnRequestTimeout, /* When the requested action didn't get response in specified time */ 74 msi_OnError, /* On protocol error */
82 msi_OnPeerTimeout, /* Peer timed out; stop the call */ 75 msi_OnPeerTimeout, /* Peer timed out; stop the call */
83 msi_OnPeerCSChange, /* Peer requested Csettings change */ 76 msi_OnCapabilities, /* Peer requested capabilities change */
84 msi_OnSelfCSChange /* Csettings change confirmation */
85} MSICallbackID; 77} MSICallbackID;
86 78
87/** 79/**
88 * Errors 80 * The call struct. Please do not modify outside msi.c
89 */ 81 */
90typedef enum { 82typedef struct MSICall_s {
91 msi_ErrorNoCall = -20, /* Trying to perform call action while not in a call */ 83 struct MSISession_s *session; /* Session pointer */
92 msi_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/
93 msi_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */
94 msi_ErrorReachedCallLimit = -23, /* Cannot handle more calls */
95} MSIError;
96
97/**
98 * The call struct.
99 */
100typedef struct _MSICall { /* Call info structure */
101 struct _MSISession *session; /* Session pointer */
102
103 MSICallState state;
104
105 MSICSettings csettings_local; /* Local call settings */
106 MSICSettings *csettings_peer; /* Peers call settings */
107
108 MSICallIDType id; /* Random value identifying the call */
109
110 int ringing_tout_ms; /* Ringing timeout in ms */
111 84
112 int request_timer_id; /* Timer id for outgoing request/action */ 85 MSICallState state;
113 int ringing_timer_id; /* Timer id for ringing timeout */ 86 uint8_t peer_capabilities; /* Peer capabilities */
87 uint8_t self_capabilities; /* Self capabilities */
88 uint16_t peer_vfpsz; /* Video frame piece size */
89 uint32_t friend_number; /* Index of this call in MSISession */
90 MSIError error; /* Last error */
114 91
115 uint32_t *peers; 92 void *av_call; /* Pointer to av call handler */
116 uint16_t peer_count;
117 93
118 int32_t call_idx; /* Index of this call in MSISession */ 94 struct MSICall_s *next;
95 struct MSICall_s *prev;
119} MSICall; 96} MSICall;
120 97
121 98
122/** 99/**
123 * Control session struct 100 * Expected return on success is 0, if any other number is
101 * returned the call is considered errored and will be handled
102 * as such which means it will be terminated without any notice.
124 */ 103 */
125typedef struct _MSISession { 104typedef int msi_action_cb (void *av, MSICall *call);
126 105
106/**
107 * Control session struct. Please do not modify outside msi.c
108 */
109typedef struct MSISession_s {
127 /* Call handlers */ 110 /* Call handlers */
128 MSICall **calls; 111 MSICall **calls;
129 int32_t max_calls; 112 uint32_t calls_tail;
130 113 uint32_t calls_head;
131 void *agent_handler;
132 Messenger *messenger_handle;
133 114
134 uint32_t frequ; 115 void *av;
135 uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */ 116 Messenger *messenger;
136 117
137 pthread_mutex_t mutex[1]; 118 pthread_mutex_t mutex[1];
138 119 msi_action_cb *callbacks[7];
139 void *timer_handler;
140 PAIR(MSICallbackType, void *) callbacks[10];
141} MSISession; 120} MSISession;
142 121
143/** 122/**
144 * Start the control session. 123 * Start the control session.
145 */ 124 */
146MSISession *msi_new ( Messenger *messenger, int32_t max_calls ); 125MSISession *msi_new(Messenger *m);
147
148/** 126/**
149 * Terminate control session. 127 * Terminate control session. NOTE: all calls will be freed
150 */ 128 */
151int msi_kill ( MSISession *session ); 129int msi_kill(MSISession *session);
152
153/** 130/**
154 * Callback setter. 131 * Callback setter.
155 */ 132 */
156void msi_register_callback(MSISession *session, MSICallbackType callback, MSICallbackID id, void *userdata); 133void msi_register_callback(MSISession *session, msi_action_cb *callback, MSICallbackID id);
157
158/**
159 * Send invite request to friend_id.
160 */
161int msi_invite ( MSISession *session,
162 int32_t *call_index,
163 const MSICSettings *csettings,
164 uint32_t rngsec,
165 uint32_t friend_id );
166
167/**
168 * Hangup active call.
169 */
170int msi_hangup ( MSISession *session, int32_t call_index );
171
172/** 134/**
173 * Answer active call request. 135 * Send invite request to friend_number.
174 */ 136 */
175int msi_answer ( MSISession *session, int32_t call_index, const MSICSettings *csettings ); 137int msi_invite(MSISession *session, MSICall **call, uint32_t friend_number, uint8_t capabilities);
176
177/** 138/**
178 * Cancel request. 139 * Hangup call. NOTE: 'call' will be freed
179 */ 140 */
180int msi_cancel ( MSISession *session, int32_t call_index, uint32_t peer, const char *reason ); 141int msi_hangup(MSICall *call);
181
182/** 142/**
183 * Reject incoming call. 143 * Answer call request.
184 */ 144 */
185int msi_reject ( MSISession *session, int32_t call_index, const char *reason ); 145int msi_answer(MSICall *call, uint8_t capabilities);
186
187/**
188 * Terminate the call.
189 */
190int msi_stopcall ( MSISession *session, int32_t call_index );
191
192/**
193 * Change codec settings of the current call.
194 */
195int msi_change_csettings ( MSISession *session, int32_t call_index, const MSICSettings *csettings );
196
197/** 146/**
198 * Main msi loop 147 * Change capabilities of the call.
199 */ 148 */
200void msi_do( MSISession *session ); 149int msi_change_capabilities(MSICall *call, uint8_t capabilities);
201 150
202#endif /* __TOXMSI */ 151#endif /* MSI_H */
diff --git a/toxav/rtp.c b/toxav/rtp.c
index 95d3bf3d..763166cd 100644
--- a/toxav/rtp.c
+++ b/toxav/rtp.c
@@ -1,6 +1,6 @@
1/** rtp.c 1/** rtp.c
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -24,505 +24,369 @@
24#endif /* HAVE_CONFIG_H */ 24#endif /* HAVE_CONFIG_H */
25 25
26#include "rtp.h" 26#include "rtp.h"
27#include "bwcontroler.h"
27#include "../toxcore/logger.h" 28#include "../toxcore/logger.h"
28#include "../toxcore/util.h" 29#include "../toxcore/util.h"
30#include "../toxcore/Messenger.h"
29 31
30#include <stdlib.h> 32#include <stdlib.h>
31void queue_message(RTPSession *_session, RTPMessage *_msg); 33#include <assert.h>
32
33#define size_32 4
34
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)
37#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0)
38#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0)
39#define ADD_SETTING_MARKER(_h, _v) do { if ( _v > 1 ) _v = 1; ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0)
40#define ADD_SETTING_PAYLOAD(_h, _v) do { if ( _v > 127 ) _v = 127; ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0)
41
42#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6)
43#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5)
44#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4)
45#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f )
46#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7)
47#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
48
49/**
50 * Checks if message came in late.
51 */
52static int check_late_message (RTPSession *session, RTPMessage *msg)
53{
54 /*
55 * Check Sequence number. If this new msg has lesser number then the session->rsequnum
56 * it shows that the message came in late. Also check timestamp to be 100% certain.
57 *
58 */
59 return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1;
60}
61 34
62 35
63/** 36int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object);
64 * Extracts header from payload.
65 */
66RTPHeader *extract_header ( const uint8_t *payload, int length )
67{
68 if ( !payload || !length ) {
69 LOGGER_WARNING("No payload to extract!");
70 return NULL;
71 }
72
73 RTPHeader *retu = calloc(1, sizeof (RTPHeader));
74 37
75 if ( !retu ) {
76 LOGGER_WARNING("Alloc failed! Program might misbehave!");
77 return NULL;
78 }
79 38
80 memcpy(&retu->sequnum, payload, sizeof(retu->sequnum)); 39RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friendnumber,
81 retu->sequnum = ntohs(retu->sequnum); 40 BWControler *bwc, void *cs,
82 41 int (*mcb) (void *, struct RTPMessage *))
83 const uint8_t *it = payload + 2;
84
85 retu->flags = *it;
86 ++it;
87
88 /* This indicates if the first 2 bits are valid.
89 * Now it may happen that this is out of order but
90 * it cuts down chances of parsing some invalid value
91 */
92
93 if ( GET_FLAG_VERSION(retu) != RTP_VERSION ) {
94 /* Deallocate */
95 LOGGER_WARNING("Invalid version!");
96 free(retu);
97 return NULL;
98 }
99
100 /*
101 * Added a check for the size of the header little sooner so
102 * I don't need to parse the other stuff if it's bad
103 */
104 uint8_t cc = GET_FLAG_CSRCC ( retu );
105 int total = 12 /* Minimum header len */ + ( cc * 4 );
106
107 if ( length < total ) {
108 /* Deallocate */
109 LOGGER_WARNING("Length invalid!");
110 free(retu);
111 return NULL;
112 }
113
114 memset(retu->csrc, 0, 16 * sizeof (uint32_t));
115
116 retu->marker_payloadt = *it;
117 ++it;
118 retu->length = total;
119
120
121 memcpy(&retu->timestamp, it, sizeof(retu->timestamp));
122 retu->timestamp = ntohl(retu->timestamp);
123 it += 4;
124 memcpy(&retu->ssrc, it, sizeof(retu->ssrc));
125 retu->ssrc = ntohl(retu->ssrc);
126
127 uint8_t x;
128
129 for ( x = 0; x < cc; x++ ) {
130 it += 4;
131 memcpy(&retu->csrc[x], it, sizeof(retu->csrc[x]));
132 retu->csrc[x] = ntohl(retu->csrc[x]);
133 }
134
135 return retu;
136}
137
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{ 42{
143 const uint8_t *it = payload; 43 assert(mcb);
44 assert(cs);
45 assert(m);
144 46
145 RTPExtHeader *retu = calloc(1, sizeof (RTPExtHeader)); 47 RTPSession *retu = calloc(1, sizeof(RTPSession));
146 48
147 if ( !retu ) { 49 if (!retu) {
148 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 50 LOGGER_WARNING("Alloc failed! Program might misbehave!");
149 return NULL; 51 return NULL;
150 } 52 }
151 53
152 uint16_t ext_length; 54 retu->ssrc = random_int();
153 memcpy(&ext_length, it, sizeof(ext_length)); 55 retu->payload_type = payload_type;
154 ext_length = ntohs(ext_length);
155 it += 2;
156 56
57 retu->m = m;
58 retu->friend_number = friendnumber;
157 59
158 if ( length < ( ext_length * sizeof(uint32_t) ) ) { 60 /* Also set payload type as prefix */
159 LOGGER_WARNING("Length invalid!");
160 free(retu);
161 return NULL;
162 }
163 61
164 retu->length = ext_length; 62 retu->bwc = bwc;
165 memcpy(&retu->type, it, sizeof(retu->type)); 63 retu->cs = cs;
166 retu->type = ntohs(retu->type); 64 retu->mcb = mcb;
167 it += 2;
168 65
169 if ( !(retu->table = calloc(ext_length, sizeof (uint32_t))) ) { 66 if (-1 == rtp_allow_receiving(retu)) {
170 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 67 LOGGER_WARNING("Failed to start rtp receiving mode");
171 free(retu); 68 free(retu);
172 return NULL; 69 return NULL;
173 } 70 }
174 71
175 uint16_t x;
176
177 for ( x = 0; x < ext_length; x++ ) {
178 it += 4;
179 memcpy(&(retu->table[x]), it, sizeof(retu->table[x]));
180 retu->table[x] = ntohl(retu->table[x]);
181 }
182
183 return retu; 72 return retu;
184} 73}
185 74void rtp_kill (RTPSession *session)
186/**
187 * Adds header to payload. Make sure _payload_ has enough space.
188 */
189uint8_t *add_header ( RTPHeader *header, uint8_t *payload )
190{ 75{
191 uint8_t cc = GET_FLAG_CSRCC ( header ); 76 if (!session)
192 uint8_t *it = payload; 77 return;
193 uint16_t sequnum;
194 uint32_t timestamp;
195 uint32_t ssrc;
196 uint32_t csrc;
197
198
199 /* Add sequence number first */
200 sequnum = htons(header->sequnum);
201 memcpy(it, &sequnum, sizeof(sequnum));
202 it += 2;
203
204 *it = header->flags;
205 ++it;
206 *it = header->marker_payloadt;
207 ++it;
208
209
210 timestamp = htonl(header->timestamp);
211 memcpy(it, &timestamp, sizeof(timestamp));
212 it += 4;
213 ssrc = htonl(header->ssrc);
214 memcpy(it, &ssrc, sizeof(ssrc));
215
216 uint8_t x;
217 78
218 for ( x = 0; x < cc; x++ ) { 79 LOGGER_DEBUG("Terminated RTP session: %p", session);
219 it += 4;
220 csrc = htonl(header->csrc[x]);
221 memcpy(it, &csrc, sizeof(csrc));
222 }
223
224 return it + 4;
225}
226
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{
232 uint8_t *it = payload;
233 uint16_t length;
234 uint16_t type;
235 uint32_t entry;
236
237 length = htons(header->length);
238 memcpy(it, &length, sizeof(length));
239 it += 2;
240 type = htons(header->type);
241 memcpy(it, &type, sizeof(type));
242 it -= 2; /* Return to 0 position */
243
244 if ( header->table ) {
245
246 uint16_t x;
247
248 for ( x = 0; x < header->length; x++ ) {
249 it += 4;
250 entry = htonl(header->table[x]);
251 memcpy(it, &entry, sizeof(entry));
252 }
253 }
254
255 return it + 4;
256}
257
258/**
259 * Builds header from control session values.
260 */
261RTPHeader *build_header ( RTPSession *session )
262{
263 RTPHeader *retu = calloc ( 1, sizeof (RTPHeader) );
264
265 if ( !retu ) {
266 LOGGER_WARNING("Alloc failed! Program might misbehave!");
267 return NULL;
268 }
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
277 retu->sequnum = session->sequnum;
278 retu->timestamp = current_time_monotonic(); /* milliseconds */
279 retu->ssrc = session->ssrc;
280
281 int i;
282
283 for ( i = 0; i < session->cc; i++ )
284 retu->csrc[i] = session->csrc[i];
285
286 retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
287 80
288 return retu; 81 rtp_stop_receiving (session);
82 free (session);
289} 83}
290 84int rtp_allow_receiving(RTPSession *session)
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{ 85{
298 RTPMessage *retu = calloc(1, sizeof (RTPMessage)); 86 if (session == NULL)
299 87 return -1;
300 retu->header = extract_header ( data, length ); /* It allocates memory and all */
301
302 if ( !retu->header ) {
303 LOGGER_WARNING("Header failed to extract!");
304 free(retu);
305 return NULL;
306 }
307
308 uint16_t from_pos = retu->header->length;
309 retu->length = length - from_pos;
310
311
312
313 if ( GET_FLAG_EXTENSION ( retu->header ) ) {
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 88
328 if ( length - from_pos <= MAX_RTP_SIZE ) 89 if (m_callback_rtp_packet(session->m, session->friend_number, session->payload_type,
329 memcpy ( retu->data, data + from_pos, length - from_pos ); 90 handle_rtp_packet, session) == -1) {
330 else { 91 LOGGER_WARNING("Failed to register rtp receive handler");
331 LOGGER_WARNING("Invalid length!"); 92 return -1;
332 rtp_free_msg(NULL, retu);
333 return NULL;
334 } 93 }
335 94
336 retu->next = NULL; 95 LOGGER_DEBUG("Started receiving on session: %p", session);
337 96 return 0;
338 return retu;
339} 97}
340 98int rtp_stop_receiving(RTPSession *session)
341/**
342 * Callback for networking core.
343 */
344int rtp_handle_packet ( Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object )
345{ 99{
346 RTPSession *session = object; 100 if (session == NULL)
347 RTPMessage *msg;
348
349 if ( !session || length < 13 ) { /* 12 is the minimum length for rtp + desc. byte */
350 LOGGER_WARNING("No session or invalid length of received buffer!");
351 return -1; 101 return -1;
352 }
353
354 msg = msg_parse ( data + 1, length - 1 );
355
356 if ( !msg ) {
357 LOGGER_WARNING("Could not parse message!");
358 return -1;
359 }
360 102
361 /* Check if message came in late */ 103 m_callback_rtp_packet(session->m, session->friend_number, session->payload_type, NULL, NULL);
362 if ( check_late_message(session, msg) < 0 ) { /* Not late */
363 session->rsequnum = msg->header->sequnum;
364 session->timestamp = msg->header->timestamp;
365 }
366
367 queue_message(session, msg);
368 104
105 LOGGER_DEBUG("Stopped receiving on session: %p", session);
369 return 0; 106 return 0;
370} 107}
371 108int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length)
372/**
373 * Allocate message and store data there
374 */
375RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t length )
376{ 109{
377 if ( !session ) { 110 if (!session) {
378 LOGGER_WARNING("No session!"); 111 LOGGER_WARNING("No session!");
379 return NULL; 112 return -1;
380 } 113 }
381 114
382 uint8_t *from_pos; 115 uint8_t rdata[length + sizeof(struct RTPHeader) + 1];
383 RTPMessage *retu = calloc(1, sizeof (RTPMessage)); 116 memset(rdata, 0, sizeof(rdata));
384 117
385 if ( !retu ) { 118 rdata[0] = session->payload_type;
386 LOGGER_WARNING("Alloc failed! Program might misbehave!");
387 return NULL;
388 }
389 119
390 /* Sets header values and copies the extension header in retu */ 120 struct RTPHeader *header = (struct RTPHeader *)(rdata + 1);
391 retu->header = build_header ( session ); /* It allocates memory and all */
392 retu->ext_header = session->ext_header;
393 121
122 header->ve = 2;
123 header->pe = 0;
124 header->xe = 0;
125 header->cc = 0;
394 126
395 uint32_t total_length = length + retu->header->length + 1; 127 header->ma = 0;
128 header->pt = session->payload_type % 128;
396 129
397 retu->data[0] = session->prefix; 130 header->sequnum = htons(session->sequnum);
131 header->timestamp = htonl(current_time_monotonic());
132 header->ssrc = htonl(session->ssrc);
398 133
399 if ( retu->ext_header ) { 134 header->cpart = 0;
400 total_length += ( 4 /* Minimum ext header len */ + retu->ext_header->length * size_32 ); 135 header->tlen = htons(length);
401 136
402 from_pos = add_header ( retu->header, retu->data + 1 ); 137 if (MAX_CRYPTO_DATA_SIZE > length + sizeof(struct RTPHeader) + 1) {
403 from_pos = add_ext_header ( retu->ext_header, from_pos + 1 );
404 } else {
405 from_pos = add_header ( retu->header, retu->data + 1 );
406 }
407 138
408 /* 139 /**
409 * Parses the extension header into the message 140 * The lenght is lesser than the maximum allowed lenght (including header)
410 * Of course if any 141 * Send the packet in single piece.
411 */ 142 */
412 143
413 /* Appends data on to retu->data */ 144 memcpy(rdata + 1 + sizeof(struct RTPHeader), data, length);
414 memcpy ( from_pos, data, length );
415 145
416 retu->length = total_length; 146 if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata, sizeof(rdata)))
147 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s", sizeof(rdata), strerror(errno));
148 } else {
417 149
418 retu->next = NULL; 150 /**
151 * The lenght is greater than the maximum allowed lenght (including header)
152 * Send the packet in multiple pieces.
153 */
419 154
420 return retu; 155 uint16_t sent = 0;
421} 156 uint16_t piece = MAX_CRYPTO_DATA_SIZE - (sizeof(struct RTPHeader) + 1);
422 157
158 while ((length - sent) + sizeof(struct RTPHeader) + 1 > MAX_CRYPTO_DATA_SIZE) {
159 memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
423 160
161 if (-1 == send_custom_lossy_packet(session->m, session->friend_number,
162 rdata, piece + sizeof(struct RTPHeader) + 1))
163 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
164 piece + sizeof(struct RTPHeader) + 1, strerror(errno));
424 165
425int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length ) 166 sent += piece;
426{ 167 header->cpart = htons(sent);
427 RTPMessage *msg = rtp_new_message (session, data, length); 168 }
428 169
429 if ( !msg ) return -1; 170 /* Send remaining */
171 piece = length - sent;
430 172
431 int ret = send_custom_lossy_packet(messenger, session->dest, msg->data, msg->length); 173 if (piece) {
174 memcpy(rdata + 1 + sizeof(struct RTPHeader), data + sent, piece);
432 175
433 if ( 0 != ret) { 176 if (-1 == send_custom_lossy_packet(session->m, session->friend_number, rdata,
434 LOGGER_WARNING("Failed to send full packet (len: %d)! error: %i", length, ret); 177 piece + sizeof(struct RTPHeader) + 1))
435 rtp_free_msg ( session, msg ); 178 LOGGER_WARNING("RTP send failed (len: %d)! std error: %s",
436 return rtp_ErrorSending; 179 piece + sizeof(struct RTPHeader) + 1, strerror(errno));
180 }
437 } 181 }
438 182
439 /* Set sequ number */ 183 session->sequnum ++;
440 session->sequnum = session->sequnum >= MAX_SEQU_NUM ? 0 : session->sequnum + 1;
441 rtp_free_msg ( session, msg );
442
443 return 0; 184 return 0;
444} 185}
445 186
446void rtp_free_msg ( RTPSession *session, RTPMessage *msg ) 187
188bool chloss (const RTPSession *session, const struct RTPHeader *header)
447{ 189{
448 if ( !session ) { 190 if (ntohl(header->timestamp) < session->rtimestamp) {
449 if ( msg->ext_header ) { 191 uint16_t hosq, lost = 0;
450 free ( msg->ext_header->table ); 192
451 free ( msg->ext_header ); 193 hosq = ntohs(header->sequnum);
452 } 194
453 } else { 195 lost = (hosq > session->rsequnum) ?
454 if ( msg->ext_header && session->ext_header != msg->ext_header ) { 196 (session->rsequnum + 65535) - hosq :
455 free ( msg->ext_header->table ); 197 session->rsequnum - hosq;
456 free ( msg->ext_header ); 198
457 } 199 puts ("Lost packet");
200 while (lost --)
201 bwc_add_lost(session->bwc ,0);
202
203 return true;
458 } 204 }
459 205
460 free ( msg->header ); 206 return false;
461 free ( msg );
462} 207}
463 208struct RTPMessage *new_message (size_t allocate_len, const uint8_t *data, uint16_t data_length)
464RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num )
465{ 209{
466 RTPSession *retu = calloc(1, sizeof(RTPSession)); 210 assert(allocate_len >= data_length);
467
468 if ( !retu ) {
469 LOGGER_WARNING("Alloc failed! Program might misbehave!");
470 return NULL;
471 }
472 211
473 if ( -1 == m_callback_rtp_packet(messenger, friend_num, payload_type, rtp_handle_packet, retu)) { 212 struct RTPMessage *msg = calloc(sizeof(struct RTPMessage) + (allocate_len - sizeof(struct RTPHeader)), 1);
474 LOGGER_ERROR("Error setting custom register handler for rtp session");
475 free(retu);
476 return NULL;
477 }
478 213
479 LOGGER_DEBUG("Registered packet handler: pt: %d; fid: %d", payload_type, friend_num); 214 msg->len = data_length - sizeof(struct RTPHeader);
215 memcpy(&msg->header, data, data_length);
480 216
481 retu->version = RTP_VERSION; /* It's always 2 */ 217 msg->header.sequnum = ntohs(msg->header.sequnum);
482 retu->padding = 0; /* If some additional data is needed about the packet */ 218 msg->header.timestamp = ntohl(msg->header.timestamp);
483 retu->extension = 0; /* If extension to header is needed */ 219 msg->header.ssrc = ntohl(msg->header.ssrc);
484 retu->cc = 1; /* Amount of contributors */
485 retu->csrc = NULL; /* Container */
486 retu->ssrc = random_int();
487 retu->marker = 0;
488 retu->payload_type = payload_type % 128;
489 220
490 retu->dest = friend_num; 221 msg->header.cpart = ntohs(msg->header.cpart);
222 msg->header.tlen = ntohs(msg->header.tlen);
491 223
492 retu->rsequnum = retu->sequnum = 0; 224 return msg;
225}
226int handle_rtp_packet (Messenger *m, uint32_t friendnumber, const uint8_t *data, uint16_t length, void *object)
227{
228 (void) m;
229 (void) friendnumber;
493 230
494 retu->ext_header = NULL; /* When needed allocate */ 231 RTPSession *session = object;
495 232
233 data ++;
234 length--;
496 235
497 if ( !(retu->csrc = calloc(1, sizeof (uint32_t))) ) { 236 if (!session || length < sizeof (struct RTPHeader)) {
498 LOGGER_WARNING("Alloc failed! Program might misbehave!"); 237 LOGGER_WARNING("No session or invalid length of received buffer!");
499 free(retu); 238 return -1;
500 return NULL;
501 } 239 }
502 240
503 retu->csrc[0] = retu->ssrc; /* Set my ssrc to the list receive */ 241 const struct RTPHeader *header = (struct RTPHeader *) data;
504 242
505 /* Also set payload type as prefix */ 243 if (header->pt != session->payload_type % 128) {
506 retu->prefix = payload_type; 244 LOGGER_WARNING("Invalid payload type with the session");
245 return -1;
246 }
507 247
508 /* 248 if (ntohs(header->cpart) >= ntohs(header->tlen)) {
509 * 249 /* Never allow this case to happen */
510 */ 250 return -1;
511 return retu; 251 }
512}
513 252
514void rtp_kill ( RTPSession *session, Messenger *messenger ) 253 bwc_feed_avg(session->bwc, length);
515{ 254
516 if ( !session ) return; 255 if (ntohs(header->tlen) == length - sizeof (struct RTPHeader)) {
256 /* The message is sent in single part */
257
258 /* Only allow messages which have arrived in order;
259 * drop late messages
260 */
261 if (chloss(session, header)) {
262 return 0;
263 } else {
264 /* Message is not late; pick up the latest parameters */
265 session->rsequnum = ntohs(header->sequnum);
266 session->rtimestamp = ntohl(header->timestamp);
267 }
517 268
518 m_callback_rtp_packet(messenger, session->dest, session->prefix, NULL, NULL); 269 bwc_add_recv(session->bwc, length);
270
271 /* Invoke processing of active multiparted message */
272 if (session->mp) {
273 if (session->mcb)
274 session->mcb (session->cs, session->mp);
275 else
276 free(session->mp);
519 277
520 free ( session->ext_header ); 278 session->mp = NULL;
521 free ( session->csrc ); 279 }
522 280
523 LOGGER_DEBUG("Terminated RTP session: %p", session); 281 /* The message came in the allowed time;
282 * process it only if handler for the session is present.
283 */
524 284
525 /* And finally free session */ 285 if (!session->mcb)
526 free ( session ); 286 return 0;
527 287
288 return session->mcb (session->cs, new_message(length, data, length));
289 } else {
290 /* The message is sent in multiple parts */
291
292 if (session->mp) {
293 /* There are 2 possible situations in this case:
294 * 1) being that we got the part of already processing message.
295 * 2) being that we got the part of a new/old message.
296 *
297 * We handle them differently as we only allow a single multiparted
298 * processing message
299 */
300
301 if (session->mp->header.sequnum == ntohs(header->sequnum) &&
302 session->mp->header.timestamp == ntohl(header->timestamp)) {
303 /* First case */
304
305 /* Make sure we have enough allocated memory */
306 if (session->mp->header.tlen - session->mp->len < length - sizeof(struct RTPHeader) ||
307 session->mp->header.tlen <= ntohs(header->cpart)) {
308 /* There happened to be some corruption on the stream;
309 * continue wihtout this part
310 */
311 return 0;
312 }
313
314 memcpy(session->mp->data + ntohs(header->cpart), data + sizeof(struct RTPHeader),
315 length - sizeof(struct RTPHeader));
316
317 session->mp->len += length - sizeof(struct RTPHeader);
318
319 bwc_add_recv(session->bwc, length);
320
321 if (session->mp->len == session->mp->header.tlen) {
322 /* Received a full message; now push it for the further
323 * processing.
324 */
325 if (session->mcb)
326 session->mcb (session->cs, session->mp);
327 else
328 free(session->mp);
329
330 session->mp = NULL;
331 }
332 } else {
333 /* Second case */
334
335 if (session->mp->header.timestamp > ntohl(header->timestamp))
336 /* The received message part is from the old message;
337 * discard it.
338 */
339 return 0;
340
341 /* Measure missing parts of the old message */
342 bwc_add_lost(session->bwc,
343 (session->mp->header.tlen - session->mp->len) +
344
345 /* Must account sizes of rtp headers too */
346 ((session->mp->header.tlen - session->mp->len) /
347 MAX_CRYPTO_DATA_SIZE) * sizeof(struct RTPHeader) );
348
349 /* Push the previous message for processing */
350 if (session->mcb)
351 session->mcb (session->cs, session->mp);
352 else
353 free(session->mp);
354
355 session->mp = NULL;
356 goto NEW_MULTIPARTED;
357 }
358 } else {
359 /* In this case threat the message as if it was received in order
360 */
361
362 /* This is also a point for new multiparted messages */
363NEW_MULTIPARTED:
364
365 /* Only allow messages which have arrived in order;
366 * drop late messages
367 */
368 if (chloss(session, header)) {
369 return 0;
370 } else {
371 /* Message is not late; pick up the latest parameters */
372 session->rsequnum = ntohs(header->sequnum);
373 session->rtimestamp = ntohl(header->timestamp);
374 }
375
376 bwc_add_recv(session->bwc, length);
377
378 /* Again, only store message if handler is present
379 */
380 if (session->mcb) {
381 session->mp = new_message(ntohs(header->tlen) + sizeof(struct RTPHeader), data, length);
382
383 /* Reposition data if necessary */
384 if (ntohs(header->cpart));
385
386 memmove(session->mp->data + ntohs(header->cpart), session->mp->data, session->mp->len);
387 }
388 }
389 }
390
391 return 0;
528} 392}
diff --git a/toxav/rtp.h b/toxav/rtp.h
index c98840ac..0393ac27 100644
--- a/toxav/rtp.h
+++ b/toxav/rtp.h
@@ -1,6 +1,6 @@
1/** rtp.h 1/** rtp.h
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -19,109 +19,91 @@
19 * 19 *
20 */ 20 */
21 21
22#ifndef __TOXRTP 22#ifndef RTP_H
23#define __TOXRTP 23#define RTP_H
24
25#define RTP_VERSION 2
26#include <inttypes.h>
27// #include <pthread.h>
28 24
25#include "bwcontroler.h"
29#include "../toxcore/Messenger.h" 26#include "../toxcore/Messenger.h"
30 27#include "stdbool.h"
31#define MAX_SEQU_NUM 65535
32#define MAX_RTP_SIZE 65535
33
34typedef enum {
35 rtp_ErrorSending = -40
36} RTPError;
37/**
38 * Standard rtp header
39 */
40typedef struct _RTPHeader {
41 uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
42 uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */
43 uint16_t sequnum; /* Sequence Number */
44 uint32_t timestamp; /* Timestamp */
45 uint32_t ssrc; /* SSRC */
46 uint32_t csrc[16]; /* CSRC's table */
47 uint32_t length; /* Length of the header in payload string. */
48
49} RTPHeader;
50 28
51/** 29/**
52 * Standard rtp extension header. 30 * Payload type identifier. Also used as rtp callback prefix.
53 */ 31 */
54typedef struct _RTPExtHeader { 32enum {
55 uint16_t type; /* Extension profile */ 33 rtp_TypeAudio = 192,
56 uint16_t length; /* Number of extensions */ 34 rtp_TypeVideo,
57 uint32_t *table; /* Extension's table */ 35};
58 36
59} RTPExtHeader; 37struct RTPHeader {
60 38 /* Standard RTP header */
61/** 39#ifndef WORDS_BIGENDIAN
62 * Standard rtp message. 40 uint16_t cc: 4; /* Contributing sources count */
63 */ 41 uint16_t xe: 1; /* Extra header */
64typedef struct _RTPMessage { 42 uint16_t pe: 1; /* Padding */
65 RTPHeader *header; 43 uint16_t ve: 2; /* Version */
66 RTPExtHeader *ext_header; 44
67 45 uint16_t pt: 7; /* Payload type */
68 uint8_t data[MAX_RTP_SIZE]; 46 uint16_t ma: 1; /* Marker */
69 uint32_t length; 47#else
70 48 uint16_t ve: 2; /* Version */
71 struct _RTPMessage *next; 49 uint16_t pe: 1; /* Padding */
72} RTPMessage; 50 uint16_t xe: 1; /* Extra header */
51 uint16_t cc: 4; /* Contributing sources count */
52
53 uint16_t ma: 1; /* Marker */
54 uint16_t pt: 7; /* Payload type */
55#endif
56
57 uint16_t sequnum;
58 uint32_t timestamp;
59 uint32_t ssrc;
60 uint32_t csrc[16];
61
62 /* Non-standard TOX-specific fields */
63 uint16_t cpart;/* Data offset of the current part */
64 uint16_t tlen; /* Total message lenght */
65} __attribute__ ((packed));
66
67/* Check alignment */
68typedef char __fail_if_misaligned [ sizeof(struct RTPHeader) == 80 ? 1 : -1 ];
69
70struct RTPMessage {
71 uint16_t len;
72
73 struct RTPHeader header;
74 uint8_t data[];
75} __attribute__ ((packed));
76
77/* Check alignment */
78typedef char __fail_if_misaligned [ sizeof(struct RTPMessage) == 82 ? 1 : -1 ];
73 79
74/** 80/**
75 * RTP control session. 81 * RTP control session.
76 */ 82 */
77typedef struct _RTPSession { 83typedef struct {
78 uint8_t version; 84 uint8_t payload_type;
79 uint8_t padding; 85 uint16_t sequnum; /* Sending sequence number */
80 uint8_t extension; 86 uint16_t rsequnum; /* Receiving sequence number */
81 uint8_t cc; 87 uint32_t rtimestamp;
82 uint8_t marker; 88 uint32_t ssrc;
83 uint8_t payload_type;
84 uint16_t sequnum; /* Set when sending */
85 uint16_t rsequnum; /* Check when recving msg */
86 uint32_t timestamp;
87 uint32_t ssrc;
88 uint32_t *csrc;
89
90 /* If some additional data must be sent via message
91 * apply it here. Only by allocating this member you will be
92 * automatically placing it within a message.
93 */
94 RTPExtHeader *ext_header;
95
96 /* Msg prefix for core to know when recving */
97 uint8_t prefix;
98
99 int dest;
100
101 struct _CSSession *cs;
102 89
103} RTPSession; 90 struct RTPMessage *mp; /* Expected parted message */
104
105/**
106 * Must be called before calling any other rtp function.
107 */
108RTPSession *rtp_new ( int payload_type, Messenger *messenger, int friend_num );
109
110/**
111 * Terminate the session.
112 */
113void rtp_kill ( RTPSession *session, Messenger *messenger );
114 91
115/** 92 Messenger *m;
116 * Sends msg to _RTPSession::dest 93 uint32_t friend_number;
117 */
118int rtp_send_msg ( RTPSession *session, Messenger *messenger, const uint8_t *data, uint16_t length );
119 94
120/** 95 BWControler *bwc;
121 * Dealloc msg. 96 void *cs;
122 */ 97 int (*mcb) (void *, struct RTPMessage *msg);
123void rtp_free_msg ( RTPSession *session, RTPMessage *msg ); 98} RTPSession;
124 99
125 100
101RTPSession *rtp_new (int payload_type, Messenger *m, uint32_t friend_num,
102 BWControler *bwc, void *cs,
103 int (*mcb) (void *, struct RTPMessage *));
104void rtp_kill (RTPSession *session);
105int rtp_allow_receiving (RTPSession *session);
106int rtp_stop_receiving (RTPSession *session);
107int rtp_send_data (RTPSession *session, const uint8_t *data, uint16_t length);
126 108
127#endif /* __TOXRTP */ 109#endif /* RTP_H */
diff --git a/toxav/toxav.c b/toxav/toxav.c
index a51ec5e3..6a17f55d 100644
--- a/toxav/toxav.c
+++ b/toxav/toxav.c
@@ -1,6 +1,6 @@
1/** toxav.c 1/** toxav.c
2 * 2 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
4 * 4 *
5 * This file is part of Tox. 5 * This file is part of Tox.
6 * 6 *
@@ -23,15 +23,10 @@
23#include "config.h" 23#include "config.h"
24#endif /* HAVE_CONFIG_H */ 24#endif /* HAVE_CONFIG_H */
25 25
26#define TOX_DEFINED
27typedef struct Messenger Tox;
28
29#define _GNU_SOURCE /* implicit declaration warning */
30
31#include "codec.h"
32#include "msi.h" 26#include "msi.h"
33#include "group.h" 27#include "rtp.h"
34 28
29#include "../toxcore/Messenger.h"
35#include "../toxcore/logger.h" 30#include "../toxcore/logger.h"
36#include "../toxcore/util.h" 31#include "../toxcore/util.h"
37 32
@@ -39,659 +34,1220 @@ typedef struct Messenger Tox;
39#include <stdlib.h> 34#include <stdlib.h>
40#include <string.h> 35#include <string.h>
41 36
42/* Assume 24 fps*/
43#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000) 37#define MAX_ENCODE_TIME_US ((1000 / 24) * 1000)
44 38
45/* true if invalid call index */ 39typedef struct ToxAVCall_s {
46#define CALL_INVALID_INDEX(idx, max) (idx < 0 || idx >= max) 40 ToxAV *av;
47 41
48const ToxAvCSettings av_DefaultSettings = { 42 pthread_mutex_t mutex_audio[1];
49 av_TypeAudio, 43 PAIR(RTPSession *, ACSession *) audio;
50 44
51 500, 45 pthread_mutex_t mutex_video[1];
52 1280, 46 PAIR(RTPSession *, VCSession *) video;
53 720,
54 47
55 32000, 48 BWControler *bwc;
56 20, 49
57 48000, 50 bool active;
58 1 51 MSICall *msi_call;
59}; 52 uint32_t friend_number;
53
54 uint32_t audio_bit_rate; /* Sending audio bit rate */
55 uint32_t video_bit_rate; /* Sending video bit rate */
56
57 /** Required for monitoring changes in states */
58 uint8_t previous_self_capabilities;
59
60 pthread_mutex_t mutex[1];
61
62 struct ToxAVCall_s *prev;
63 struct ToxAVCall_s *next;
64} ToxAVCall;
60 65
61static const uint32_t jbuf_capacity = 6; 66struct ToxAV {
62static const uint8_t audio_index = 0, video_index = 1; 67 Messenger *m;
68 MSISession *msi;
63 69
64typedef struct _ToxAvCall { 70 /* Two-way storage: first is array of calls and second is list of calls with head and tail */
71 ToxAVCall **calls;
72 uint32_t calls_tail;
73 uint32_t calls_head;
65 pthread_mutex_t mutex[1]; 74 pthread_mutex_t mutex[1];
66 pthread_mutex_t mutex_encoding_audio[1]; 75
67 pthread_mutex_t mutex_encoding_video[1]; 76 PAIR(toxav_call_cb *, void *) ccb; /* Call callback */
68 pthread_mutex_t mutex_do[1]; 77 PAIR(toxav_call_state_cb *, void *) scb; /* Call state callback */
69 RTPSession *crtps[2]; /** Audio is first and video is second */ 78 PAIR(toxav_audio_receive_frame_cb *, void *) acb; /* Audio frame receive callback */
70 CSSession *cs; 79 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
71 _Bool active; 80 PAIR(toxav_bit_rate_status_cb *, void *) bcb; /* Bit rate control callback */
72} ToxAvCall; 81
73 82 /** Decode time measures */
74struct _ToxAv { 83 int32_t dmssc; /** Measure count */
75 Messenger *messenger; 84 int32_t dmsst; /** Last cycle total */
76 MSISession *msi_session; /** Main msi session */ 85 int32_t dmssa; /** Average decoding time in ms */
77 ToxAvCall *calls; /** Per-call params */ 86
78 uint32_t max_calls; 87 uint32_t interval; /** Calculated interval */
79
80 PAIR(ToxAvAudioCallback, void *) acb;
81 PAIR(ToxAvVideoCallback, void *) vcb;
82
83 /* Decode time measure */
84 int32_t dectmsscount; /** Measure count */
85 int32_t dectmsstotal; /** Last cycle total */
86 int32_t avgdectms; /** Average decoding time in ms */
87}; 88};
88 89
89static const MSICSettings *msicsettings_cast (const ToxAvCSettings *from) 90void callback_bwc (BWControler *bwc, uint32_t friend_number, float loss, void *user_data);
91
92int callback_invite(void *toxav_inst, MSICall *call);
93int callback_start(void *toxav_inst, MSICall *call);
94int callback_end(void *toxav_inst, MSICall *call);
95int callback_error(void *toxav_inst, MSICall *call);
96int callback_capabilites(void *toxav_inst, MSICall *call);
97
98bool audio_bit_rate_invalid(uint32_t bit_rate);
99bool video_bit_rate_invalid(uint32_t bit_rate);
100bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state);
101ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error);
102ToxAVCall *call_get(ToxAV *av, uint32_t friend_number);
103ToxAVCall *call_remove(ToxAVCall *call);
104bool call_prepare_transmission(ToxAVCall *call);
105void call_kill_transmission(ToxAVCall *call);
106
107uint32_t toxav_version_major(void)
90{ 108{
91 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); 109 return 0;
92 return (const MSICSettings *) from;
93} 110}
94 111uint32_t toxav_version_minor(void)
95static const ToxAvCSettings *toxavcsettings_cast (const MSICSettings *from)
96{ 112{
97 assert(sizeof(MSICSettings) == sizeof(ToxAvCSettings)); 113 return 0;
98 return (const ToxAvCSettings *) from;
99
100} 114}
115uint32_t toxav_version_patch(void)
116{
117 return 0;
118}
119bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch)
120{
121 (void)major;
122 (void)minor;
123 (void)patch;
101 124
102ToxAv *toxav_new( Tox *messenger, int32_t max_calls) 125 return 1;
126}
127ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error)
103{ 128{
104 ToxAv *av = calloc ( sizeof(ToxAv), 1); 129 TOXAV_ERR_NEW rc = TOXAV_ERR_NEW_OK;
130 ToxAV *av = NULL;
105 131
106 if (av == NULL) { 132 if (tox == NULL) {
107 LOGGER_WARNING("Allocation failed!"); 133 rc = TOXAV_ERR_NEW_NULL;
108 return NULL; 134 goto END;
109 } 135 }
110 136
111 av->messenger = (Messenger *)messenger; 137 if (((Messenger *)tox)->msi_packet) {
112 av->msi_session = msi_new(av->messenger, max_calls); 138 rc = TOXAV_ERR_NEW_MULTIPLE;
113 av->msi_session->agent_handler = av; 139 goto END;
114 av->calls = calloc(sizeof(ToxAvCall), max_calls); 140 }
115 av->max_calls = max_calls;
116 141
117 unsigned int i; 142 av = calloc (sizeof(ToxAV), 1);
118 143
119 for (i = 0; i < max_calls; ++i) { 144 if (av == NULL) {
120 if (create_recursive_mutex(av->calls[i].mutex) != 0 ) { 145 LOGGER_WARNING("Allocation failed!");
121 LOGGER_WARNING("Failed to init call(%u) mutex!", i); 146 rc = TOXAV_ERR_NEW_MALLOC;
122 msi_kill(av->msi_session); 147 goto END;
148 }
123 149
124 free(av->calls); 150 if (create_recursive_mutex(av->mutex) != 0) {
125 free(av); 151 LOGGER_WARNING("Mutex creation failed!");
126 return NULL; 152 rc = TOXAV_ERR_NEW_MALLOC;
127 } 153 goto END;
128 } 154 }
129 155
130 return av; 156 av->m = (Messenger *)tox;
131} 157 av->msi = msi_new(av->m);
132 158
133void toxav_kill ( ToxAv *av ) 159 if (av->msi == NULL) {
134{ 160 pthread_mutex_destroy(av->mutex);
135 uint32_t i; 161 rc = TOXAV_ERR_NEW_MALLOC;
162 goto END;
163 }
136 164
137 for (i = 0; i < av->max_calls; i ++) { 165 av->interval = 200;
138 if ( av->calls[i].crtps[audio_index] ) 166 av->msi->av = av;
139 rtp_kill(av->calls[i].crtps[audio_index], av->msi_session->messenger_handle);
140 167
168 msi_register_callback(av->msi, callback_invite, msi_OnInvite);
169 msi_register_callback(av->msi, callback_start, msi_OnStart);
170 msi_register_callback(av->msi, callback_end, msi_OnEnd);
171 msi_register_callback(av->msi, callback_error, msi_OnError);
172 msi_register_callback(av->msi, callback_error, msi_OnPeerTimeout);
173 msi_register_callback(av->msi, callback_capabilites, msi_OnCapabilities);
141 174
142 if ( av->calls[i].crtps[video_index] ) 175END:
143 rtp_kill(av->calls[i].crtps[video_index], av->msi_session->messenger_handle);
144 176
145 if ( av->calls[i].cs ) 177 if (error)
146 cs_kill(av->calls[i].cs); 178 *error = rc;
147 179
148 pthread_mutex_destroy(av->calls[i].mutex); 180 if (rc != TOXAV_ERR_NEW_OK) {
181 free(av);
182 av = NULL;
149 } 183 }
150 184
151 msi_kill(av->msi_session); 185 return av;
152
153 free(av->calls);
154 free(av);
155} 186}
156 187void toxav_kill(ToxAV *av)
157uint32_t toxav_do_interval(ToxAv *av)
158{ 188{
159 int i = 0; 189 if (av == NULL)
160 uint32_t rc = 200 + av->avgdectms; /* Return 200 if no call is active */ 190 return;
191
192 pthread_mutex_lock(av->mutex);
193
194 /* To avoid possible deadlocks */
195 while (av->msi && msi_kill(av->msi) != 0) {
196 pthread_mutex_unlock(av->mutex);
197 pthread_mutex_lock(av->mutex);
198 }
161 199
162 for (; i < av->max_calls; i ++) { 200 /* Msi kill will hang up all calls so just clean these calls */
163 pthread_mutex_lock(av->calls[i].mutex); 201 if (av->calls) {
202 ToxAVCall *it = call_get(av, av->calls_head);
164 203
165 if (av->calls[i].active) { 204 while (it) {
166 /* This should work. Video payload will always come in greater intervals */ 205 call_kill_transmission(it);
167 rc = MIN(av->calls[i].cs->audio_decoder_frame_duration, rc); 206 it = call_remove(it); /* This will eventually free av->calls */
168 } 207 }
169
170 pthread_mutex_unlock(av->calls[i].mutex);
171 } 208 }
172 209
173 return rc < av->avgdectms ? 0 : rc - av->avgdectms; 210 pthread_mutex_unlock(av->mutex);
211 pthread_mutex_destroy(av->mutex);
212
213 free(av);
174} 214}
175 215Tox *toxav_get_tox(const ToxAV *av)
176void toxav_do(ToxAv *av) 216{
217 return (Tox *) av->m;
218}
219uint32_t toxav_iteration_interval(const ToxAV *av)
220{
221 /* If no call is active interval is 200 */
222 return av->calls ? av->interval : 200;
223}
224void toxav_iterate(ToxAV *av)
177{ 225{
178 msi_do(av->msi_session); 226 pthread_mutex_lock(av->mutex);
227
228 if (av->calls == NULL) {
229 pthread_mutex_unlock(av->mutex);
230 return;
231 }
179 232
180 uint64_t start = current_time_monotonic(); 233 uint64_t start = current_time_monotonic();
234 int32_t rc = 500;
181 235
182 uint32_t i = 0; 236 ToxAVCall *i = av->calls[av->calls_head];
183 237
184 for (; i < av->max_calls; i ++) { 238 for (; i; i = i->next) {
185 pthread_mutex_lock(av->calls[i].mutex); 239 if (i->active) {
240 pthread_mutex_lock(i->mutex);
241 pthread_mutex_unlock(av->mutex);
186 242
187 if (av->calls[i].active) { 243 ac_iterate(i->audio.second);
188 pthread_mutex_lock(av->calls[i].mutex_do); 244 vc_iterate(i->video.second);
189 pthread_mutex_unlock(av->calls[i].mutex); 245
190 cs_do(av->calls[i].cs); 246 if (i->msi_call->self_capabilities & msi_CapRAudio &&
191 pthread_mutex_unlock(av->calls[i].mutex_do); 247 i->msi_call->peer_capabilities & msi_CapSAudio)
192 } else { 248 rc = MIN(i->audio.second->lp_frame_duration, rc);
193 pthread_mutex_unlock(av->calls[i].mutex); 249
250 if (i->msi_call->self_capabilities & msi_CapRVideo &&
251 i->msi_call->peer_capabilities & msi_CapSVideo)
252 rc = MIN(i->video.second->lcfd, (uint32_t) rc);
253
254 uint32_t fid = i->friend_number;
255
256 pthread_mutex_unlock(i->mutex);
257 pthread_mutex_lock(av->mutex);
258
259 /* In case this call is popped from container stop iteration */
260 if (call_get(av, fid) != i)
261 break;
194 } 262 }
195 } 263 }
196 264
197 uint64_t end = current_time_monotonic(); 265 pthread_mutex_unlock(av->mutex);
198 266
199 /* TODO maybe use variable for sizes */ 267 av->interval = rc < av->dmssa ? 0 : (rc - av->dmssa);
200 av->dectmsstotal += end - start; 268 av->dmsst += current_time_monotonic() - start;
201 269
202 if (++av->dectmsscount == 3) { 270 if (++av->dmssc == 3) {
203 av->avgdectms = av->dectmsstotal / 3 + 2 /* NOTE Magic Offset */; 271 av->dmssa = av->dmsst / 3 + 5 /* NOTE Magic Offset for precission */;
204 av->dectmsscount = 0; 272 av->dmssc = 0;
205 av->dectmsstotal = 0; 273 av->dmsst = 0;
206 } 274 }
207} 275}
208 276bool toxav_call(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
209void toxav_register_callstate_callback ( ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata ) 277 TOXAV_ERR_CALL *error)
210{ 278{
211 msi_register_callback(av->msi_session, (MSICallbackType)cb, (MSICallbackID) id, userdata); 279 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
212} 280
281 pthread_mutex_lock(av->mutex);
282
283 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate))
284 || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))) {
285 rc = TOXAV_ERR_CALL_INVALID_BIT_RATE;
286 goto END;
287 }
213 288
214void toxav_register_audio_callback(ToxAv *av, ToxAvAudioCallback cb, void *userdata) 289 ToxAVCall *call = call_new(av, friend_number, error);
215{
216 av->acb.first = cb;
217 av->acb.second = userdata;
218}
219 290
220void toxav_register_video_callback(ToxAv *av, ToxAvVideoCallback cb, void *userdata) 291 if (call == NULL) {
221{ 292 rc = TOXAV_ERR_CALL_MALLOC;
222 av->vcb.first = cb; 293 goto END;
223 av->vcb.second = userdata; 294 }
224}
225 295
226int toxav_call (ToxAv *av, 296 call->audio_bit_rate = audio_bit_rate;
227 int32_t *call_index, 297 call->video_bit_rate = video_bit_rate;
228 int user,
229 const ToxAvCSettings *csettings,
230 int ringing_seconds )
231{
232 return msi_invite(av->msi_session, call_index, msicsettings_cast(csettings), ringing_seconds * 1000, user);
233}
234 298
235int toxav_hangup ( ToxAv *av, int32_t call_index ) 299 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
236{
237 return msi_hangup(av->msi_session, call_index);
238}
239 300
240int toxav_answer ( ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ) 301 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
241{ 302 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
242 return msi_answer(av->msi_session, call_index, msicsettings_cast(csettings));
243}
244 303
245int toxav_reject ( ToxAv *av, int32_t call_index, const char *reason ) 304 if (msi_invite(av->msi, &call->msi_call, friend_number, call->previous_self_capabilities) != 0) {
246{ 305 call_remove(call);
247 return msi_reject(av->msi_session, call_index, reason); 306 rc = TOXAV_ERR_CALL_SYNC;
248} 307 goto END;
308 }
249 309
250int toxav_cancel ( ToxAv *av, int32_t call_index, int peer_id, const char *reason ) 310 call->msi_call->av_call = call;
251{
252 return msi_cancel(av->msi_session, call_index, peer_id, reason);
253}
254 311
255int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings) 312END:
256{ 313 pthread_mutex_unlock(av->mutex);
257 return msi_change_csettings(av->msi_session, call_index, msicsettings_cast(csettings)); 314
258} 315 if (error)
316 *error = rc;
259 317
260int toxav_stop_call ( ToxAv *av, int32_t call_index ) 318 return rc == TOXAV_ERR_CALL_OK;
319}
320void toxav_callback_call(ToxAV *av, toxav_call_cb *function, void *user_data)
261{ 321{
262 return msi_stopcall(av->msi_session, call_index); 322 pthread_mutex_lock(av->mutex);
323 av->ccb.first = function;
324 av->ccb.second = user_data;
325 pthread_mutex_unlock(av->mutex);
263} 326}
264 327bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate,
265int toxav_prepare_transmission ( ToxAv *av, int32_t call_index, int support_video ) 328 TOXAV_ERR_ANSWER *error)
266{ 329{
267 if ( !av->msi_session || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || 330 pthread_mutex_lock(av->mutex);
268 !av->msi_session->calls[call_index] || !av->msi_session->calls[call_index]->csettings_peer) {
269 LOGGER_ERROR("Error while starting RTP session: invalid call!\n");
270 return av_ErrorNoCall;
271 }
272 331
273 ToxAvCall *call = &av->calls[call_index]; 332 TOXAV_ERR_ANSWER rc = TOXAV_ERR_ANSWER_OK;
274 333
275 pthread_mutex_lock(call->mutex); 334 if (m_friend_exists(av->m, friend_number) == 0) {
276 335 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND;
277 if (call->active) { 336 goto END;
278 pthread_mutex_unlock(call->mutex);
279 LOGGER_ERROR("Error while starting RTP session: call already active!\n");
280 return av_ErrorAlreadyInCallWithPeer;
281 } 337 }
282 338
283 if (pthread_mutex_init(call->mutex_encoding_audio, NULL) != 0 339 if ((audio_bit_rate && audio_bit_rate_invalid(audio_bit_rate))
284 || pthread_mutex_init(call->mutex_encoding_video, NULL) != 0 || pthread_mutex_init(call->mutex_do, NULL) != 0) { 340 || (video_bit_rate && video_bit_rate_invalid(video_bit_rate))
285 pthread_mutex_unlock(call->mutex); 341 ) {
286 LOGGER_ERROR("Error while starting RTP session: mutex initializing failed!\n"); 342 rc = TOXAV_ERR_ANSWER_INVALID_BIT_RATE;
287 return av_ErrorUnknown; 343 goto END;
288 } 344 }
289 345
290 const ToxAvCSettings *c_peer = toxavcsettings_cast 346 ToxAVCall *call = call_get(av, friend_number);
291 (&av->msi_session->calls[call_index]->csettings_peer[0]);
292 const ToxAvCSettings *c_self = toxavcsettings_cast
293 (&av->msi_session->calls[call_index]->csettings_local);
294 347
295 LOGGER_DEBUG( 348 if (call == NULL) {
296 "Type: %u(s) %u(p)\n" 349 rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING;
297 "Video bitrate: %u(s) %u(p)\n" 350 goto END;
298 "Video height: %u(s) %u(p)\n" 351 }
299 "Video width: %u(s) %u(p)\n"
300 "Audio bitrate: %u(s) %u(p)\n"
301 "Audio framedur: %u(s) %u(p)\n"
302 "Audio sample rate: %u(s) %u(p)\n"
303 "Audio channels: %u(s) %u(p)\n",
304 c_self->call_type, c_peer->call_type,
305 c_self->video_bitrate, c_peer->video_bitrate,
306 c_self->max_video_height, c_peer->max_video_height,
307 c_self->max_video_width, c_peer->max_video_width,
308 c_self->audio_bitrate, c_peer->audio_bitrate,
309 c_self->audio_frame_duration, c_peer->audio_frame_duration,
310 c_self->audio_sample_rate, c_peer->audio_sample_rate,
311 c_self->audio_channels, c_peer->audio_channels );
312 352
313 if ( !(call->cs = cs_new(c_self, c_peer, jbuf_capacity, support_video)) ) { 353 if (!call_prepare_transmission(call)) {
314 LOGGER_ERROR("Error while starting Codec State!\n"); 354 rc = TOXAV_ERR_ANSWER_CODEC_INITIALIZATION;
315 pthread_mutex_unlock(call->mutex); 355 goto END;
316 return av_ErrorInitializingCodecs;
317 } 356 }
318 357
319 call->cs->agent = av; 358 call->audio_bit_rate = audio_bit_rate;
320 call->cs->call_idx = call_index; 359 call->video_bit_rate = video_bit_rate;
360
361 call->previous_self_capabilities = msi_CapRAudio | msi_CapRVideo;
321 362
322 call->cs->acb.first = av->acb.first; 363 call->previous_self_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
323 call->cs->acb.second = av->acb.second; 364 call->previous_self_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
324 365
325 call->cs->vcb.first = av->vcb.first; 366 if (msi_answer(call->msi_call, call->previous_self_capabilities) != 0)
326 call->cs->vcb.second = av->vcb.second; 367 rc = TOXAV_ERR_ANSWER_SYNC;
327 368
369END:
370 pthread_mutex_unlock(av->mutex);
328 371
329 call->crtps[audio_index] = 372 if (error)
330 rtp_new(msi_TypeAudio, av->messenger, av->msi_session->calls[call_index]->peers[0]); 373 *error = rc;
331 374
332 if ( !call->crtps[audio_index] ) { 375 return rc == TOXAV_ERR_ANSWER_OK;
333 LOGGER_ERROR("Error while starting audio RTP session!\n"); 376}
334 goto error; 377void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *function, void *user_data)
378{
379 pthread_mutex_lock(av->mutex);
380 av->scb.first = function;
381 av->scb.second = user_data;
382 pthread_mutex_unlock(av->mutex);
383}
384bool toxav_call_control(ToxAV *av, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error)
385{
386 pthread_mutex_lock(av->mutex);
387 TOXAV_ERR_CALL_CONTROL rc = TOXAV_ERR_CALL_CONTROL_OK;
388
389 if (m_friend_exists(av->m, friend_number) == 0) {
390 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND;
391 goto END;
335 } 392 }
336 393
337 call->crtps[audio_index]->cs = call->cs; 394 ToxAVCall *call = call_get(av, friend_number);
338 395
339 if ( support_video ) { 396 if (call == NULL || (!call->active && control != TOXAV_CALL_CONTROL_CANCEL)) {
340 call->crtps[video_index] = 397 rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
341 rtp_new(msi_TypeVideo, av->messenger, av->msi_session->calls[call_index]->peers[0]); 398 goto END;
399 }
342 400
343 if ( !call->crtps[video_index] ) { 401 switch (control) {
344 LOGGER_ERROR("Error while starting video RTP session!\n"); 402 case TOXAV_CALL_CONTROL_RESUME: {
345 goto error; 403 /* Only act if paused and had media transfer active before */
404 if (call->msi_call->self_capabilities == 0 &&
405 call->previous_self_capabilities) {
406
407 if (msi_change_capabilities(call->msi_call,
408 call->previous_self_capabilities) == -1) {
409 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
410 goto END;
411 }
412
413 rtp_allow_receiving(call->audio.first);
414 rtp_allow_receiving(call->video.first);
415 } else {
416 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
417 goto END;
418 }
419 }
420 break;
421
422 case TOXAV_CALL_CONTROL_PAUSE: {
423 /* Only act if not already paused */
424 if (call->msi_call->self_capabilities) {
425 call->previous_self_capabilities = call->msi_call->self_capabilities;
426
427 if (msi_change_capabilities(call->msi_call, 0) == -1) {
428 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
429 goto END;
430 }
431
432 rtp_stop_receiving(call->audio.first);
433 rtp_stop_receiving(call->video.first);
434 } else {
435 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
436 goto END;
437 }
346 } 438 }
439 break;
440
441 case TOXAV_CALL_CONTROL_CANCEL: {
442 /* Hang up */
443 pthread_mutex_lock(call->mutex);
444 if (msi_hangup(call->msi_call) != 0) {
445 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
446 pthread_mutex_unlock(call->mutex);
447 goto END;
448 }
449
450 call->msi_call = NULL;
451 pthread_mutex_unlock(call->mutex);
347 452
348 call->crtps[video_index]->cs = call->cs; 453 /* No mather the case, terminate the call */
454 call_kill_transmission(call);
455 call_remove(call);
456 }
457 break;
458
459 case TOXAV_CALL_CONTROL_MUTE_AUDIO: {
460 if (call->msi_call->self_capabilities & msi_CapRAudio) {
461 if (msi_change_capabilities(call->msi_call, call->
462 msi_call->self_capabilities ^ msi_CapRAudio) == -1) {
463 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
464 goto END;
465 }
466
467 rtp_stop_receiving(call->audio.first);
468 } else {
469 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
470 goto END;
471 }
472 }
473 break;
474
475 case TOXAV_CALL_CONTROL_UNMUTE_AUDIO: {
476 if (call->msi_call->self_capabilities ^ msi_CapRAudio) {
477 if (msi_change_capabilities(call->msi_call, call->
478 msi_call->self_capabilities | msi_CapRAudio) == -1) {
479 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
480 goto END;
481 }
482
483 rtp_allow_receiving(call->audio.first);
484 } else {
485 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
486 goto END;
487 }
488 }
489 break;
490
491 case TOXAV_CALL_CONTROL_HIDE_VIDEO: {
492 if (call->msi_call->self_capabilities & msi_CapRVideo) {
493 if (msi_change_capabilities(call->msi_call, call->
494 msi_call->self_capabilities ^ msi_CapRVideo) == -1) {
495 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
496 goto END;
497 }
498
499 rtp_stop_receiving(call->video.first);
500 } else {
501 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
502 goto END;
503 }
504 }
505 break;
506
507 case TOXAV_CALL_CONTROL_SHOW_VIDEO: {
508 if (call->msi_call->self_capabilities ^ msi_CapRVideo) {
509 if (msi_change_capabilities(call->msi_call, call->
510 msi_call->self_capabilities | msi_CapRVideo) == -1) {
511 rc = TOXAV_ERR_CALL_CONTROL_SYNC;
512 goto END;
513 }
514
515 rtp_allow_receiving(call->audio.first);
516 } else {
517 rc = TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION;
518 goto END;
519 }
520 }
521 break;
349 } 522 }
350 523
351 call->active = 1; 524END:
352 pthread_mutex_unlock(call->mutex); 525 pthread_mutex_unlock(av->mutex);
353 return av_ErrorNone;
354error:
355 rtp_kill(call->crtps[audio_index], av->messenger);
356 call->crtps[audio_index] = NULL;
357 rtp_kill(call->crtps[video_index], av->messenger);
358 call->crtps[video_index] = NULL;
359 cs_kill(call->cs);
360 call->cs = NULL;
361 call->active = 0;
362 pthread_mutex_destroy(call->mutex_encoding_audio);
363 pthread_mutex_destroy(call->mutex_encoding_video);
364 pthread_mutex_destroy(call->mutex_do);
365 526
366 pthread_mutex_unlock(call->mutex); 527 if (error)
367 return av_ErrorCreatingRtpSessions; 528 *error = rc;
368}
369 529
370int toxav_kill_transmission ( ToxAv *av, int32_t call_index ) 530 return rc == TOXAV_ERR_CALL_CONTROL_OK;
531}
532bool toxav_bit_rate_set(ToxAV *av, uint32_t friend_number, int32_t audio_bit_rate,
533 int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error)
371{ 534{
372 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 535 TOXAV_ERR_BIT_RATE_SET rc = TOXAV_ERR_BIT_RATE_SET_OK;
373 LOGGER_WARNING("Invalid call index: %d", call_index); 536 ToxAVCall *call;
374 return av_ErrorNoCall; 537
538 if (m_friend_exists(av->m, friend_number) == 0) {
539 rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND;
540 goto END;
375 } 541 }
376 542
377 ToxAvCall *call = &av->calls[call_index]; 543 if (audio_bit_rate > 0 && audio_bit_rate_invalid(audio_bit_rate)) {
544 rc = TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE;
545 goto END;
546 }
378 547
379 pthread_mutex_lock(call->mutex); 548 if (video_bit_rate > 0 && video_bit_rate_invalid(video_bit_rate)) {
549 rc = TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE;
550 goto END;
551 }
380 552
381 if (!call->active) { 553 pthread_mutex_lock(av->mutex);
382 pthread_mutex_unlock(call->mutex); 554 call = call_get(av, friend_number);
383 LOGGER_WARNING("Action on inactive call: %d", call_index); 555
384 return av_ErrorInvalidState; 556 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
557 pthread_mutex_unlock(av->mutex);
558 rc = TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL;
559 goto END;
385 } 560 }
386 561
387 call->active = 0; 562 if (audio_bit_rate >= 0) {
563 LOGGER_DEBUG("Setting new audio bitrate to: %d", audio_bit_rate);
564
565 if (call->audio_bit_rate == audio_bit_rate) {
566 LOGGER_DEBUG("Audio bitrate already set to: %d", audio_bit_rate);
567 } else if (audio_bit_rate == 0) {
568 LOGGER_DEBUG("Turned off audio sending");
569 if (msi_change_capabilities(call->msi_call, call->msi_call->
570 self_capabilities ^ msi_CapSAudio) != 0) {
571 pthread_mutex_unlock(av->mutex);
572 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
573 goto END;
574 }
575 /* Audio sending is turned off; notify peer */
576 call->audio_bit_rate = 0;
577 } else {
578 pthread_mutex_lock(call->mutex);
579 if (call->audio_bit_rate == 0) {
580 LOGGER_DEBUG("Turned on audio sending");
581 /* The audio has been turned off before this */
582 if (msi_change_capabilities(call->msi_call, call->
583 msi_call->self_capabilities | msi_CapSAudio) != 0) {
584 pthread_mutex_unlock(call->mutex);
585 pthread_mutex_unlock(av->mutex);
586 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
587 goto END;
588 }
589 } else
590 LOGGER_DEBUG("Set new audio bit rate %d", audio_bit_rate);
591 call->audio_bit_rate = audio_bit_rate;
592 pthread_mutex_unlock(call->mutex);
593 }
594 }
388 595
389 pthread_mutex_lock(call->mutex_encoding_audio); 596 if (video_bit_rate >= 0) {
390 pthread_mutex_unlock(call->mutex_encoding_audio); 597 LOGGER_DEBUG("Setting new video bitrate to: %d", video_bit_rate);
391 pthread_mutex_lock(call->mutex_encoding_video); 598
392 pthread_mutex_unlock(call->mutex_encoding_video); 599 if (call->video_bit_rate == video_bit_rate) {
393 pthread_mutex_lock(call->mutex_do); 600 LOGGER_DEBUG("Video bitrate already set to: %d", video_bit_rate);
394 pthread_mutex_unlock(call->mutex_do); 601 } else if (video_bit_rate == 0) {
602 LOGGER_DEBUG("Turned off video sending");
603 /* Video sending is turned off; notify peer */
604 if (msi_change_capabilities(call->msi_call, call->msi_call->
605 self_capabilities ^ msi_CapSVideo) != 0) {
606 pthread_mutex_unlock(av->mutex);
607 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
608 goto END;
609 }
610 call->video_bit_rate = 0;
611 } else {
612 pthread_mutex_lock(call->mutex);
613 if (call->video_bit_rate == 0) {
614 LOGGER_DEBUG("Turned on video sending");
615 /* The video has been turned off before this */
616 if (msi_change_capabilities(call->msi_call, call->
617 msi_call->self_capabilities | msi_CapSVideo) != 0) {
618 pthread_mutex_unlock(call->mutex);
619 pthread_mutex_unlock(av->mutex);
620 rc = TOXAV_ERR_BIT_RATE_SET_SYNC;
621 goto END;
622 }
623 } else
624 LOGGER_DEBUG("Set new video bit rate %d", video_bit_rate);
625 call->video_bit_rate = video_bit_rate;
626 pthread_mutex_unlock(call->mutex);
627 }
628 }
629
630 pthread_mutex_unlock(av->mutex);
631END:
632 if (error)
633 *error = rc;
395 634
396 rtp_kill(call->crtps[audio_index], av->messenger); 635 return rc == TOXAV_ERR_BIT_RATE_SET_OK;
397 call->crtps[audio_index] = NULL; 636}
398 rtp_kill(call->crtps[video_index], av->messenger); 637void toxav_callback_bit_rate_status(ToxAV *av, toxav_bit_rate_status_cb *function, void *user_data)
399 call->crtps[video_index] = NULL; 638{
400 cs_kill(call->cs); 639 pthread_mutex_lock(av->mutex);
401 call->cs = NULL; 640 av->bcb.first = function;
641 av->bcb.second = user_data;
642 pthread_mutex_unlock(av->mutex);
643}
644bool toxav_audio_send_frame(ToxAV *av, uint32_t friend_number, const int16_t *pcm, size_t sample_count,
645 uint8_t channels, uint32_t sampling_rate, TOXAV_ERR_SEND_FRAME *error)
646{
647 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
648 ToxAVCall *call;
402 649
403 pthread_mutex_destroy(call->mutex_encoding_audio); 650 if (m_friend_exists(av->m, friend_number) == 0) {
404 pthread_mutex_destroy(call->mutex_encoding_video); 651 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
405 pthread_mutex_destroy(call->mutex_do); 652 goto END;
653 }
406 654
407 pthread_mutex_unlock(call->mutex); 655 if (pthread_mutex_trylock(av->mutex) != 0) {
656 rc = TOXAV_ERR_SEND_FRAME_SYNC;
657 goto END;
658 }
659
660 call = call_get(av, friend_number);
408 661
409 return av_ErrorNone; 662 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
410} 663 pthread_mutex_unlock(av->mutex);
664 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
665 goto END;
666 }
411 667
412static int toxav_send_rtp_payload(ToxAv *av, 668 if (call->audio_bit_rate == 0 ||
413 ToxAvCall *call, 669 !(call->msi_call->self_capabilities & msi_CapSAudio) ||
414 ToxAvCallType type, 670 !(call->msi_call->peer_capabilities & msi_CapRAudio)) {
415 const uint8_t *payload, 671 pthread_mutex_unlock(av->mutex);
416 unsigned int length) 672 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
417{ 673 goto END;
418 if (call->crtps[type - av_TypeAudio]) { 674 }
419 675
420 /* Audio */ 676 pthread_mutex_lock(call->mutex_audio);
421 if (type == av_TypeAudio) 677 pthread_mutex_unlock(av->mutex);
422 return rtp_send_msg(call->crtps[audio_index], av->messenger, payload, length);
423 678
424 /* Video */ 679 if (pcm == NULL) {
425 int parts = cs_split_video_payload(call->cs, payload, length); 680 pthread_mutex_unlock(call->mutex_audio);
681 rc = TOXAV_ERR_SEND_FRAME_NULL;
682 goto END;
683 }
426 684
427 if (parts < 0) return parts; 685 if (channels > 2) {
686 pthread_mutex_unlock(call->mutex_audio);
687 rc = TOXAV_ERR_SEND_FRAME_INVALID;
688 goto END;
689 }
428 690
429 uint16_t part_size; 691 { /* Encode and send */
430 const uint8_t *iter; 692 if (ac_reconfigure_encoder(call->audio.second, call->audio_bit_rate * 1000, sampling_rate, channels) != 0) {
693 pthread_mutex_unlock(call->mutex_audio);
694 rc = TOXAV_ERR_SEND_FRAME_INVALID;
695 goto END;
696 }
431 697
432 int i; 698 uint8_t dest[sample_count + sizeof(sampling_rate)]; /* This is more than enough always */
433 699
434 for (i = 0; i < parts; i++) { 700 sampling_rate = htonl(sampling_rate);
435 iter = cs_get_split_video_frame(call->cs, &part_size); 701 memcpy(dest, &sampling_rate, sizeof(sampling_rate));
702 int vrc = opus_encode(call->audio.second->encoder, pcm, sample_count,
703 dest + sizeof(sampling_rate), sizeof(dest) - sizeof(sampling_rate));
436 704
437 if (rtp_send_msg(call->crtps[video_index], av->messenger, iter, part_size) < 0) 705 if (vrc < 0) {
438 return av_ErrorSendingPayload; 706 LOGGER_WARNING("Failed to encode frame %s", opus_strerror(vrc));
707 pthread_mutex_unlock(call->mutex_audio);
708 rc = TOXAV_ERR_SEND_FRAME_INVALID;
709 goto END;
439 } 710 }
440 711
441 return av_ErrorNone; 712 if (rtp_send_data(call->audio.first, dest, vrc + sizeof(sampling_rate)) != 0) {
713 LOGGER_WARNING("Failed to send audio packet");
714 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
715 }
716 }
442 717
443 } else return av_ErrorNoRtpSession;
444}
445 718
446int toxav_prepare_video_frame ( ToxAv *av, int32_t call_index, uint8_t *dest, int dest_max, vpx_image_t *input) 719 pthread_mutex_unlock(call->mutex_audio);
720
721END:
722 if (error)
723 *error = rc;
724
725 return rc == TOXAV_ERR_SEND_FRAME_OK;
726}
727bool toxav_video_send_frame(ToxAV *av, uint32_t friend_number, uint16_t width, uint16_t height, const uint8_t *y,
728 const uint8_t *u, const uint8_t *v, TOXAV_ERR_SEND_FRAME *error)
447{ 729{
448 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 730 TOXAV_ERR_SEND_FRAME rc = TOXAV_ERR_SEND_FRAME_OK;
449 LOGGER_WARNING("Invalid call index: %d", call_index); 731 ToxAVCall *call;
450 return av_ErrorNoCall; 732
733 if (m_friend_exists(av->m, friend_number) == 0) {
734 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND;
735 goto END;
451 } 736 }
452 737
738 if (pthread_mutex_trylock(av->mutex) != 0) {
739 rc = TOXAV_ERR_SEND_FRAME_SYNC;
740 goto END;
741 }
742
743 call = call_get(av, friend_number);
453 744
454 ToxAvCall *call = &av->calls[call_index]; 745 if (call == NULL || !call->active || call->msi_call->state != msi_CallActive) {
455 pthread_mutex_lock(call->mutex); 746 pthread_mutex_unlock(av->mutex);
747 rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
748 goto END;
749 }
456 750
457 if (!call->active) { 751 if (call->video_bit_rate == 0 ||
458 pthread_mutex_unlock(call->mutex); 752 !(call->msi_call->self_capabilities & msi_CapSVideo) ||
459 LOGGER_WARNING("Action on inactive call: %d", call_index); 753 !(call->msi_call->peer_capabilities & msi_CapRVideo)) {
460 return av_ErrorInvalidState; 754 pthread_mutex_unlock(av->mutex);
755 rc = TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED;
756 goto END;
461 } 757 }
462 758
463 if (!(call->cs->capabilities & cs_VideoEncoding)) { 759 pthread_mutex_lock(call->mutex_video);
464 pthread_mutex_unlock(call->mutex); 760 pthread_mutex_unlock(av->mutex);
465 LOGGER_WARNING("Call doesn't support encoding video: %d", call_index); 761
466 return av_ErrorInvalidState; 762 if (y == NULL || u == NULL || v == NULL) {
763 pthread_mutex_unlock(call->mutex_video);
764 rc = TOXAV_ERR_SEND_FRAME_NULL;
765 goto END;
467 } 766 }
468 767
469 if (cs_set_video_encoder_resolution(call->cs, input->w, input->h) < 0) { 768 if (vc_reconfigure_encoder(call->video.second, call->video_bit_rate * 1000, width, height) != 0) {
470 pthread_mutex_unlock(call->mutex); 769 pthread_mutex_unlock(call->mutex_video);
471 return av_ErrorSettingVideoResolution; 770 rc = TOXAV_ERR_SEND_FRAME_INVALID;
771 goto END;
472 } 772 }
473 773
474 pthread_mutex_lock(call->mutex_encoding_video); 774 { /* Encode */
475 pthread_mutex_unlock(call->mutex); 775 vpx_image_t img;
776 img.w = img.h = img.d_w = img.d_h = 0;
777 vpx_img_alloc(&img, VPX_IMG_FMT_I420, width, height, 0);
778
779 /* I420 "It comprises an NxM Y plane followed by (N/2)x(M/2) V and U planes."
780 * http://fourcc.org/yuv.php#IYUV
781 */
782 memcpy(img.planes[VPX_PLANE_Y], y, width * height);
783 memcpy(img.planes[VPX_PLANE_U], u, (width / 2) * (height / 2));
784 memcpy(img.planes[VPX_PLANE_V], v, (width / 2) * (height / 2));
476 785
477 int rc = vpx_codec_encode(&call->cs->v_encoder, input, call->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US); 786 int vrc = vpx_codec_encode(call->video.second->encoder, &img,
787 call->video.second->frame_counter, 1, 0, MAX_ENCODE_TIME_US);
478 788
479 if ( rc != VPX_CODEC_OK) { 789 vpx_img_free(&img);
480 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(rc)); 790
481 pthread_mutex_unlock(call->mutex_encoding_video); 791 if (vrc != VPX_CODEC_OK) {
482 return av_ErrorEncodingVideo; 792 pthread_mutex_unlock(call->mutex_video);
793 LOGGER_ERROR("Could not encode video frame: %s\n", vpx_codec_err_to_string(vrc));
794 rc = TOXAV_ERR_SEND_FRAME_INVALID;
795 goto END;
796 }
483 } 797 }
484 798
485 ++call->cs->frame_counter; 799 ++call->video.second->frame_counter;
486 800
487 vpx_codec_iter_t iter = NULL; 801 { /* Send frames */
488 const vpx_codec_cx_pkt_t *pkt; 802 vpx_codec_iter_t iter = NULL;
489 int copied = 0; 803 const vpx_codec_cx_pkt_t *pkt;
490 804
491 while ( (pkt = vpx_codec_get_cx_data(&call->cs->v_encoder, &iter)) ) { 805 while ((pkt = vpx_codec_get_cx_data(call->video.second->encoder, &iter))) {
492 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) { 806 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT &&
493 if ( copied + pkt->data.frame.sz > dest_max ) { 807 rtp_send_data(call->video.first, pkt->data.frame.buf, pkt->data.frame.sz) < 0) {
494 pthread_mutex_unlock(call->mutex_encoding_video);
495 return av_ErrorPacketTooLarge;
496 }
497 808
498 memcpy(dest + copied, pkt->data.frame.buf, pkt->data.frame.sz); 809 pthread_mutex_unlock(call->mutex_video);
499 copied += pkt->data.frame.sz; 810 LOGGER_WARNING("Could not send video frame: %s\n", strerror(errno));
811 rc = TOXAV_ERR_SEND_FRAME_RTP_FAILED;
812 goto END;
813 }
500 } 814 }
501 } 815 }
502 816
503 pthread_mutex_unlock(call->mutex_encoding_video); 817 pthread_mutex_unlock(call->mutex_video);
504 return copied; 818
505} 819END:
820 if (error)
821 *error = rc;
506 822
507int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int frame_size) 823 return rc == TOXAV_ERR_SEND_FRAME_OK;
824}
825void toxav_callback_audio_receive_frame(ToxAV *av, toxav_audio_receive_frame_cb *function, void *user_data)
826{
827 pthread_mutex_lock(av->mutex);
828 av->acb.first = function;
829 av->acb.second = user_data;
830 pthread_mutex_unlock(av->mutex);
831}
832void toxav_callback_video_receive_frame(ToxAV *av, toxav_video_receive_frame_cb *function, void *user_data)
508{ 833{
834 pthread_mutex_lock(av->mutex);
835 av->vcb.first = function;
836 av->vcb.second = user_data;
837 pthread_mutex_unlock(av->mutex);
838}
509 839
510 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) {
511 LOGGER_WARNING("Invalid call index: %d", call_index);
512 return av_ErrorNoCall;
513 }
514 840
515 ToxAvCall *call = &av->calls[call_index]; 841/*******************************************************************************
516 pthread_mutex_lock(call->mutex); 842 *
843 * :: Internal
844 *
845 ******************************************************************************/
846void callback_bwc(BWControler* bwc, uint32_t friend_number, float loss, void* user_data)
847{
848 /* Callback which is called when the internal measure mechanism reported packet loss.
849 * We report suggested lowered bitrate to an app. If app is sending both audio and video,
850 * we will report lowered bitrate for video only because in that case video probably
851 * takes more than 90% bandwidth. Otherwise, we report lowered bitrate on audio.
852 * The application may choose to disable video totally if the stream is too bad.
853 */
854
855 ToxAVCall* call = user_data;
856 assert(call);
857
858 LOGGER_DEBUG("Reported loss of %f%%", loss*100);
859
860 if (loss < .01f)
861 return;
862
863 pthread_mutex_lock(call->av->mutex);
864 if (!call->av->bcb.first) {
865 pthread_mutex_unlock(call->av->mutex);
866 LOGGER_WARNING("No callback to report loss on");
867 return;
868 }
869
870 if (call->video_bit_rate)
871 (*call->av->bcb.first) (call->av, friend_number, call->audio_bit_rate,
872 call->video_bit_rate - (call->video_bit_rate * loss),
873 call->av->bcb.second);
874 else if (call->audio_bit_rate)
875 (*call->av->bcb.first) (call->av, friend_number,
876 call->audio_bit_rate - (call->audio_bit_rate * loss),
877 0, call->av->bcb.second);
878
879 pthread_mutex_unlock(call->av->mutex);
880}
881int callback_invite(void *toxav_inst, MSICall *call)
882{
883 ToxAV *toxav = toxav_inst;
884 pthread_mutex_lock(toxav->mutex);
517 885
886 ToxAVCall *av_call = call_new(toxav, call->friend_number, NULL);
518 887
519 if (!call->active) { 888 if (av_call == NULL) {
520 pthread_mutex_unlock(call->mutex); 889 LOGGER_WARNING("Failed to initialize call...");
521 LOGGER_WARNING("Action on inactive call: %d", call_index); 890 pthread_mutex_unlock(toxav->mutex);
522 return av_ErrorInvalidState; 891 return -1;
523 } 892 }
524 893
525 int rc = toxav_send_rtp_payload(av, call, av_TypeVideo, frame, frame_size); 894 call->av_call = av_call;
526 pthread_mutex_unlock(call->mutex); 895 av_call->msi_call = call;
527 896
528 return rc; 897 if (toxav->ccb.first)
529} 898 toxav->ccb.first(toxav, call->friend_number, call->peer_capabilities & msi_CapSAudio,
899 call->peer_capabilities & msi_CapSVideo, toxav->ccb.second);
900 else {
901 /* No handler to capture the call request, send failure */
902 pthread_mutex_unlock(toxav->mutex);
903 return -1;
904 }
530 905
531int toxav_prepare_audio_frame ( ToxAv *av, 906 pthread_mutex_unlock(toxav->mutex);
532 int32_t call_index, 907 return 0;
533 uint8_t *dest, 908}
534 int dest_max, 909int callback_start(void *toxav_inst, MSICall *call)
535 const int16_t *frame,
536 int frame_size)
537{ 910{
538 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 911 ToxAV *toxav = toxav_inst;
539 LOGGER_WARNING("Action on nonexisting call: %d", call_index); 912 pthread_mutex_lock(toxav->mutex);
540 return av_ErrorNoCall;
541 }
542 913
543 ToxAvCall *call = &av->calls[call_index]; 914 ToxAVCall *av_call = call_get(toxav, call->friend_number);
544 pthread_mutex_lock(call->mutex);
545 915
546 if (!call->active) { 916 if (av_call == NULL) {
547 pthread_mutex_unlock(call->mutex); 917 /* Should this ever happen? */
548 LOGGER_WARNING("Action on inactive call: %d", call_index); 918 pthread_mutex_unlock(toxav->mutex);
549 return av_ErrorInvalidState; 919 return -1;
550 } 920 }
551 921
552 pthread_mutex_lock(call->mutex_encoding_audio); 922 if (!call_prepare_transmission(av_call)) {
553 pthread_mutex_unlock(call->mutex); 923 callback_error(toxav_inst, call);
554 int32_t rc = opus_encode(call->cs->audio_encoder, frame, frame_size, dest, dest_max); 924 pthread_mutex_unlock(toxav->mutex);
555 pthread_mutex_unlock(call->mutex_encoding_audio); 925 return -1;
926 }
556 927
557 if (rc < 0) { 928 if (!invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities)) {
558 LOGGER_ERROR("Failed to encode payload: %s\n", opus_strerror(rc)); 929 callback_error(toxav_inst, call);
559 return av_ErrorEncodingAudio; 930 pthread_mutex_unlock(toxav->mutex);
931 return -1;
560 } 932 }
561 933
562 return rc; 934 pthread_mutex_unlock(toxav->mutex);
935 return 0;
563} 936}
564 937int callback_end(void *toxav_inst, MSICall *call)
565int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *data, unsigned int size)
566{ 938{
567 if (CALL_INVALID_INDEX(call_index, av->msi_session->max_calls)) { 939 ToxAV *toxav = toxav_inst;
568 LOGGER_WARNING("Action on nonexisting call: %d", call_index); 940 pthread_mutex_lock(toxav->mutex);
569 return av_ErrorNoCall;
570 }
571
572 ToxAvCall *call = &av->calls[call_index];
573 pthread_mutex_lock(call->mutex);
574 941
942 invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_FINISHED);
575 943
576 if (!call->active) { 944 if (call->av_call) {
577 pthread_mutex_unlock(call->mutex); 945 call_kill_transmission(call->av_call);
578 LOGGER_WARNING("Action on inactive call: %d", call_index); 946 call_remove(call->av_call);
579 return av_ErrorInvalidState;
580 } 947 }
581 948
582 int rc = toxav_send_rtp_payload(av, call, av_TypeAudio, data, size); 949 pthread_mutex_unlock(toxav->mutex);
583 pthread_mutex_unlock(call->mutex); 950 return 0;
584 return rc;
585} 951}
586 952int callback_error(void *toxav_inst, MSICall *call)
587int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest )
588{ 953{
589 if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || 954 ToxAV *toxav = toxav_inst;
590 !av->msi_session->calls[call_index] || av->msi_session->calls[call_index]->peer_count <= peer ) 955 pthread_mutex_lock(toxav->mutex);
591 return av_ErrorNoCall;
592 956
593 *dest = *toxavcsettings_cast(&av->msi_session->calls[call_index]->csettings_peer[peer]); 957 invoke_call_state_callback(toxav, call->friend_number, TOXAV_FRIEND_CALL_STATE_ERROR);
594 return av_ErrorNone;
595}
596 958
597int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ) 959 if (call->av_call) {
598{ 960 call_kill_transmission(call->av_call);
599 if ( peer < 0 || CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] 961 call_remove(call->av_call);
600 || av->msi_session->calls[call_index]->peer_count <= peer ) 962 }
601 return av_ErrorNoCall;
602 963
603 return av->msi_session->calls[call_index]->peers[peer]; 964 pthread_mutex_unlock(toxav->mutex);
965 return 0;
604} 966}
605 967int callback_capabilites(void *toxav_inst, MSICall *call)
606ToxAvCallState toxav_get_call_state(ToxAv *av, int32_t call_index)
607{ 968{
608 if ( CALL_INVALID_INDEX(call_index, av->msi_session->max_calls) || !av->msi_session->calls[call_index] ) 969 ToxAV *toxav = toxav_inst;
609 return av_CallNonExistent; 970 pthread_mutex_lock(toxav->mutex);
610 971
611 return av->msi_session->calls[call_index]->state; 972 if (call->peer_capabilities & msi_CapSAudio)
973 rtp_allow_receiving(((ToxAVCall *)call->av_call)->audio.first);
974 else
975 rtp_stop_receiving(((ToxAVCall *)call->av_call)->audio.first);
612 976
613} 977 if (call->peer_capabilities & msi_CapSVideo)
978 rtp_allow_receiving(((ToxAVCall *)call->av_call)->video.first);
979 else
980 rtp_stop_receiving(((ToxAVCall *)call->av_call)->video.first);
981
982 invoke_call_state_callback(toxav, call->friend_number, call->peer_capabilities);
614 983
615int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ) 984 pthread_mutex_unlock(toxav->mutex);
985 return 0;
986}
987bool audio_bit_rate_invalid(uint32_t bit_rate)
616{ 988{
617 return av->calls[call_index].cs ? av->calls[call_index].cs->capabilities & (CSCapabilities) capability : 0; 989 /* Opus RFC 6716 section-2.1.1 dictates the following:
618 /* 0 is error here */ 990 * Opus supports all bit rates from 6 kbit/s to 510 kbit/s.
991 */
992 return bit_rate < 6 || bit_rate > 510;
619} 993}
620 994bool video_bit_rate_invalid(uint32_t bit_rate)
621Tox *toxav_get_tox(ToxAv *av)
622{ 995{
623 return (Tox *)av->messenger; 996 (void) bit_rate;
997 /* TODO: If anyone knows the answer to this one please fill it up */
998 return false;
624} 999}
1000bool invoke_call_state_callback(ToxAV *av, uint32_t friend_number, uint32_t state)
1001{
1002 if (av->scb.first)
1003 av->scb.first(av, friend_number, state, av->scb.second);
1004 else
1005 return false;
625 1006
626int toxav_get_active_count(ToxAv *av) 1007 return true;
1008}
1009ToxAVCall *call_new(ToxAV *av, uint32_t friend_number, TOXAV_ERR_CALL *error)
627{ 1010{
628 if (!av) return -1; 1011 /* Assumes mutex locked */
1012 TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
1013 ToxAVCall *call = NULL;
629 1014
630 int rc = 0, i = 0; 1015 if (m_friend_exists(av->m, friend_number) == 0) {
1016 rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND;
1017 goto END;
1018 }
631 1019
632 for (; i < av->max_calls; i++) { 1020 if (m_get_friend_connectionstatus(av->m, friend_number) < 1) {
633 pthread_mutex_lock(av->calls[i].mutex); 1021 rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED;
1022 goto END;
1023 }
634 1024
635 if (av->calls[i].active) rc++; 1025 if (call_get(av, friend_number) != NULL) {
1026 rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL;
1027 goto END;
1028 }
636 1029
637 pthread_mutex_unlock(av->calls[i].mutex); 1030
1031 call = calloc(sizeof(ToxAVCall), 1);
1032
1033 if (call == NULL) {
1034 rc = TOXAV_ERR_CALL_MALLOC;
1035 goto END;
638 } 1036 }
639 1037
640 return rc; 1038 call->av = av;
641} 1039 call->friend_number = friend_number;
642 1040
643/* Create a new toxav group. 1041 if (av->calls == NULL) { /* Creating */
644 * 1042 av->calls = calloc (sizeof(ToxAVCall *), friend_number + 1);
645 * return group number on success.
646 * return -1 on failure.
647 *
648 * Audio data callback format:
649 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
650 *
651 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
652 */
653int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int,
654 uint8_t, unsigned int, void *), void *userdata)
655{
656 Messenger *m = tox;
657 return add_av_groupchat(m->group_chat_object, audio_callback, userdata);
658}
659 1043
660/* Join a AV group (you need to have been invited first.) 1044 if (av->calls == NULL) {
661 * 1045 free(call);
662 * returns group number on success 1046 call = NULL;
663 * returns -1 on failure. 1047 rc = TOXAV_ERR_CALL_MALLOC;
664 * 1048 goto END;
665 * Audio data callback format (same as the one for toxav_add_av_groupchat()): 1049 }
666 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata) 1050
667 * 1051 av->calls_tail = av->calls_head = friend_number;
668 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 1052
669 */ 1053 } else if (av->calls_tail < friend_number) { /* Appending */
670int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, 1054 void *tmp = realloc(av->calls, sizeof(ToxAVCall *) * (friend_number + 1));
671 void (*audio_callback)(Messenger *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), 1055
672 void *userdata) 1056 if (tmp == NULL) {
1057 free(call);
1058 call = NULL;
1059 rc = TOXAV_ERR_CALL_MALLOC;
1060 goto END;
1061 }
1062
1063 av->calls = tmp;
1064
1065 /* Set fields in between to null */
1066 uint32_t i = av->calls_tail + 1;
1067
1068 for (; i < friend_number; i ++)
1069 av->calls[i] = NULL;
1070
1071 call->prev = av->calls[av->calls_tail];
1072 av->calls[av->calls_tail]->next = call;
1073
1074 av->calls_tail = friend_number;
1075
1076 } else if (av->calls_head > friend_number) { /* Inserting at front */
1077 call->next = av->calls[av->calls_head];
1078 av->calls[av->calls_head]->prev = call;
1079 av->calls_head = friend_number;
1080 }
1081
1082 av->calls[friend_number] = call;
1083
1084END:
1085
1086 if (error)
1087 *error = rc;
1088
1089 return call;
1090}
1091ToxAVCall *call_get(ToxAV *av, uint32_t friend_number)
673{ 1092{
674 Messenger *m = tox; 1093 /* Assumes mutex locked */
675 return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata); 1094 if (av->calls == NULL || av->calls_tail < friend_number)
1095 return NULL;
1096
1097 return av->calls[friend_number];
676} 1098}
1099ToxAVCall *call_remove(ToxAVCall *call)
1100{
1101 if (call == NULL)
1102 return NULL;
677 1103
678/* Send audio to the group chat. 1104 uint32_t friend_number = call->friend_number;
679 * 1105 ToxAV *av = call->av;
680 * return 0 on success. 1106
681 * return -1 on failure. 1107 ToxAVCall *prev = call->prev;
682 * 1108 ToxAVCall *next = call->next;
683 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 1109
684 * 1110 /* Set av call in msi to NULL in order to know if call if ToxAVCall is
685 * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000) 1111 * removed from the msi call.
686 * Valid number of channels are 1 or 2. 1112 */
687 * Valid sample rates are 8000, 12000, 16000, 24000, or 48000. 1113 if (call->msi_call) {
688 * 1114 call->msi_call->av_call = NULL;
689 * Recommended values are: samples = 960, channels = 1, sample_rate = 48000 1115 }
690 */ 1116
691int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels, 1117 free(call);
692 unsigned int sample_rate) 1118
1119 if (prev)
1120 prev->next = next;
1121 else if (next)
1122 av->calls_head = next->friend_number;
1123 else goto CLEAR;
1124
1125 if (next)
1126 next->prev = prev;
1127 else if (prev)
1128 av->calls_tail = prev->friend_number;
1129 else goto CLEAR;
1130
1131 av->calls[friend_number] = NULL;
1132 return next;
1133
1134CLEAR:
1135 av->calls_head = av->calls_tail = 0;
1136 free(av->calls);
1137 av->calls = NULL;
1138
1139 return NULL;
1140}
1141bool call_prepare_transmission(ToxAVCall *call)
693{ 1142{
694 Messenger *m = tox; 1143 /* Assumes mutex locked */
695 return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate); 1144
1145 if (call == NULL)
1146 return false;
1147
1148 ToxAV *av = call->av;
1149
1150 if (!av->acb.first && !av->vcb.first)
1151 /* It makes no sense to have CSession without callbacks */
1152 return false;
1153
1154 if (call->active) {
1155 LOGGER_WARNING("Call already active!\n");
1156 return true;
1157 }
1158
1159 if (create_recursive_mutex(call->mutex_audio) != 0)
1160 return false;
1161
1162 if (create_recursive_mutex(call->mutex_video) != 0)
1163 goto FAILURE_3;
1164
1165 if (create_recursive_mutex(call->mutex) != 0)
1166 goto FAILURE_2;
1167
1168 /* Prepare bwc */
1169 call->bwc = bwc_new(av->m, call->friend_number, callback_bwc, call);
1170
1171 { /* Prepare audio */
1172 call->audio.second = ac_new(av, call->friend_number, av->acb.first, av->acb.second);
1173
1174 if (!call->audio.second) {
1175 LOGGER_ERROR("Failed to create audio codec session");
1176 goto FAILURE;
1177 }
1178
1179 call->audio.first = rtp_new(rtp_TypeAudio, av->m, call->friend_number, call->bwc,
1180 call->audio.second, ac_queue_message);
1181
1182 if (!call->audio.first) {
1183 LOGGER_ERROR("Failed to create audio rtp session");;
1184 goto FAILURE;
1185 }
1186 }
1187 { /* Prepare video */
1188 call->video.second = vc_new(av, call->friend_number, av->vcb.first, av->vcb.second);
1189
1190 if (!call->video.second) {
1191 LOGGER_ERROR("Failed to create video codec session");
1192 goto FAILURE;
1193 }
1194
1195 call->video.first = rtp_new(rtp_TypeVideo, av->m, call->friend_number, call->bwc,
1196 call->video.second, vc_queue_message);
1197
1198 if (!call->video.first) {
1199 LOGGER_ERROR("Failed to create video rtp session");
1200 goto FAILURE;
1201 }
1202 }
1203
1204 call->active = 1;
1205 return true;
1206
1207FAILURE:
1208 bwc_kill(call->bwc);
1209 rtp_kill(call->audio.first);
1210 ac_kill(call->audio.second);
1211 call->audio.first = NULL;
1212 call->audio.second = NULL;
1213 rtp_kill(call->video.first);
1214 vc_kill(call->video.second);
1215 call->video.first = NULL;
1216 call->video.second = NULL;
1217 pthread_mutex_destroy(call->mutex);
1218FAILURE_2:
1219 pthread_mutex_destroy(call->mutex_video);
1220FAILURE_3:
1221 pthread_mutex_destroy(call->mutex_audio);
1222 return false;
696} 1223}
1224void call_kill_transmission(ToxAVCall *call)
1225{
1226 if (call == NULL || call->active == 0)
1227 return;
1228
1229 call->active = 0;
697 1230
1231 pthread_mutex_lock(call->mutex_audio);
1232 pthread_mutex_unlock(call->mutex_audio);
1233 pthread_mutex_lock(call->mutex_video);
1234 pthread_mutex_unlock(call->mutex_video);
1235 pthread_mutex_lock(call->mutex);
1236 pthread_mutex_unlock(call->mutex);
1237
1238 bwc_kill(call->bwc);
1239
1240 rtp_kill(call->audio.first);
1241 ac_kill(call->audio.second);
1242 call->audio.first = NULL;
1243 call->audio.second = NULL;
1244
1245 rtp_kill(call->video.first);
1246 vc_kill(call->video.second);
1247 call->video.first = NULL;
1248 call->video.second = NULL;
1249
1250 pthread_mutex_destroy(call->mutex_audio);
1251 pthread_mutex_destroy(call->mutex_video);
1252 pthread_mutex_destroy(call->mutex);
1253}
diff --git a/toxav/toxav.h b/toxav/toxav.h
index 7285f45c..5c5195b3 100644
--- a/toxav/toxav.h
+++ b/toxav/toxav.h
@@ -1,285 +1,695 @@
1/** toxav.h 1/* toxav.h
2 *
3 * Copyright (C) 2013-2015 Tox project All Rights Reserved.
2 * 4 *
3 * Copyright (C) 2013 Tox project All Rights Reserved. 5 * This file is part of Tox.
4 * 6 *
5 * This file is part of Tox. 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.
6 * 11 *
7 * Tox is free software: you can redistribute it and/or modify 12 * Tox is distributed in the hope that it will be useful,
8 * it under the terms of the GNU General Public License as published by 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * the Free Software Foundation, either version 3 of the License, or 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * (at your option) any later version. 15 * GNU General Public License for more details.
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 * 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 */ 20 */
21 21
22#ifndef TOXAV_H
23#define TOXAV_H
22 24
23#ifndef __TOXAV 25#include <stdbool.h>
24#define __TOXAV 26#include <stddef.h>
25#include <inttypes.h> 27#include <stdint.h>
26 28
27#ifdef __cplusplus 29#ifdef __cplusplus
28extern "C" { 30extern "C" {
29#endif 31#endif
30 32
31typedef struct _ToxAv ToxAv; 33/** \page av Public audio/video API for Tox clients.
32 34 *
33/* vpx_image_t */ 35 * This API can handle multiple calls. Each call has its state, in very rare
34#include <vpx/vpx_image.h> 36 * occasions the library can change the state of the call without apps knowledge.
35 37 *
36typedef void ( *ToxAVCallback ) ( void *agent, int32_t call_idx, void *arg ); 38 */
37typedef void ( *ToxAvAudioCallback ) (void *agent, int32_t call_idx, const int16_t *PCM, uint16_t size, void *data); 39/** \subsection events Events and callbacks
38typedef void ( *ToxAvVideoCallback ) (void *agent, int32_t call_idx, const vpx_image_t *img, void *data); 40 *
39 41 * As in Core API, events are handled by callbacks. One callback can be
42 * registered per event. All events have a callback function type named
43 * `toxav_{event}_cb` and a function to register it named `toxav_callback_{event}`.
44 * Passing a NULL callback will result in no callback being registered for that
45 * event. Only one callback per event can be registered, so if a client needs
46 * multiple event listeners, it needs to implement the dispatch functionality
47 * itself. Unlike Core API, lack of some event handlers will cause the the
48 * library to drop calls before they are started. Hanging up call from a
49 * callback causes undefined behaviour.
50 *
51 */
52/** \subsection threading Threading implications
53 *
54 * Unlike the Core API, this API is fully thread-safe. The library will ensure
55 * the proper synchronization of parallel calls.
56 *
57 * A common way to run ToxAV (multiple or single instance) is to have a thread,
58 * separate from tox instance thread, running a simple toxav_iterate loop,
59 * sleeping for toxav_iteration_interval * milliseconds on each iteration.
60 *
61 * An important thing to note is that events are triggered from both tox and
62 * toxav thread (see above). Audio and video receive frame events are triggered
63 * from toxav thread while all the other events are triggered from tox thread.
64 *
65 * Tox thread has priority with mutex mechanisms. Any api function can
66 * fail if mutexes are held by tox thread in which case they will set SYNC
67 * error code.
68 */
69/**
70 * External Tox type.
71 */
40#ifndef TOX_DEFINED 72#ifndef TOX_DEFINED
41#define TOX_DEFINED 73#define TOX_DEFINED
42typedef struct Tox Tox; 74typedef struct Tox Tox;
43#endif 75#endif /* TOX_DEFINED */
44
45#define RTP_PAYLOAD_SIZE 65535
46
47 76
48/** 77/**
49 * Callbacks ids that handle the call states. 78 * ToxAV.
50 */ 79 */
51typedef enum {
52 av_OnInvite, /* Incoming call */
53 av_OnRinging, /* When peer is ready to accept/reject the call */
54 av_OnStart, /* Call (RTP transmission) started */
55 av_OnCancel, /* The side that initiated call canceled invite */
56 av_OnReject, /* The side that was invited rejected the call */
57 av_OnEnd, /* Call that was active ended */
58 av_OnRequestTimeout, /* When the requested action didn't get response in specified time */
59 av_OnPeerTimeout, /* Peer timed out; stop the call */
60 av_OnPeerCSChange, /* Peer changing Csettings. Prepare for changed AV */
61 av_OnSelfCSChange /* Csettings change confirmation. Once triggered peer is ready to recv changed AV */
62} ToxAvCallbackID;
63
64
65/** 80/**
66 * Call type identifier. 81 * The ToxAV instance type. Each ToxAV instance can be bound to only one Tox
82 * instance, and Tox instance can have only one ToxAV instance. One must make
83 * sure to close ToxAV instance prior closing Tox instance otherwise undefined
84 * behaviour occurs. Upon closing of ToxAV instance, all active calls will be
85 * forcibly terminated without notifying peers.
86 *
67 */ 87 */
68typedef enum { 88#ifndef TOXAV_DEFINED
69 av_TypeAudio = 192, 89#define TOXAV_DEFINED
70 av_TypeVideo 90typedef struct ToxAV ToxAV;
71} ToxAvCallType; 91#endif /* TOXAV_DEFINED */
72
73 92
74typedef enum {
75 av_CallNonExistent = -1,
76 av_CallInviting, /* when sending call invite */
77 av_CallStarting, /* when getting call invite */
78 av_CallActive,
79 av_CallHold,
80 av_CallHungUp
81} ToxAvCallState;
82 93
94/*******************************************************************************
95 *
96 * :: API version
97 *
98 ******************************************************************************/
83/** 99/**
84 * Error indicators. Values under -20 are reserved for toxcore. 100 * The major version number. Incremented when the API or ABI changes in an
101 * incompatible way.
85 */ 102 */
86typedef enum { 103#define TOXAV_VERSION_MAJOR 0u
87 av_ErrorNone = 0,
88 av_ErrorUnknown = -1, /* Unknown error */
89 av_ErrorNoCall = -20, /* Trying to perform call action while not in a call */
90 av_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/
91 av_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */
92 av_ErrorReachedCallLimit = -23, /* Cannot handle more calls */
93 av_ErrorInitializingCodecs = -30, /* Failed creating CSSession */
94 av_ErrorSettingVideoResolution = -31, /* Error setting resolution */
95 av_ErrorSettingVideoBitrate = -32, /* Error setting bitrate */
96 av_ErrorSplittingVideoPayload = -33, /* Error splitting video payload */
97 av_ErrorEncodingVideo = -34, /* vpx_codec_encode failed */
98 av_ErrorEncodingAudio = -35, /* opus_encode failed */
99 av_ErrorSendingPayload = -40, /* Sending lossy packet failed */
100 av_ErrorCreatingRtpSessions = -41, /* One of the rtp sessions failed to initialize */
101 av_ErrorNoRtpSession = -50, /* Trying to perform rtp action on invalid session */
102 av_ErrorInvalidCodecState = -51, /* Codec state not initialized */
103 av_ErrorPacketTooLarge = -52, /* Split packet exceeds it's limit */
104} ToxAvError;
105
106 104
107/** 105/**
108 * Locally supported capabilities. 106 * The minor version number. Incremented when functionality is added without
107 * breaking the API or ABI. Set to 0 when the major version number is
108 * incremented.
109 */ 109 */
110typedef enum { 110#define TOXAV_VERSION_MINOR 0u
111 av_AudioEncoding = 1 << 0,
112 av_AudioDecoding = 1 << 1,
113 av_VideoEncoding = 1 << 2,
114 av_VideoDecoding = 1 << 3
115} ToxAvCapabilities;
116
117 111
118/** 112/**
119 * Encoding settings. 113 * The patch or revision number. Incremented when bugfixes are applied without
114 * changing any functionality or API or ABI.
120 */ 115 */
121typedef struct _ToxAvCSettings { 116#define TOXAV_VERSION_PATCH 0u
122 ToxAvCallType call_type;
123
124 uint32_t video_bitrate; /* In kbits/s */
125 uint16_t max_video_width; /* In px */
126 uint16_t max_video_height; /* In px */
127
128 uint32_t audio_bitrate; /* In bits/s */
129 uint16_t audio_frame_duration; /* In ms */
130 uint32_t audio_sample_rate; /* In Hz */
131 uint32_t audio_channels;
132} ToxAvCSettings;
133
134extern const ToxAvCSettings av_DefaultSettings;
135 117
136/** 118/**
137 * Start new A/V session. There can only be one session at the time. 119 * A macro to check at preprocessing time whether the client code is compatible
120 * with the installed version of ToxAV.
138 */ 121 */
139ToxAv *toxav_new(Tox *messenger, int32_t max_calls); 122#define TOXAV_VERSION_IS_API_COMPATIBLE(MAJOR, MINOR, PATCH) \
123 (TOXAV_VERSION_MAJOR == MAJOR && \
124 (TOXAV_VERSION_MINOR > MINOR || \
125 (TOXAV_VERSION_MINOR == MINOR && \
126 TOXAV_VERSION_PATCH >= PATCH)))
140 127
141/** 128/**
142 * Remove A/V session. 129 * A macro to make compilation fail if the client code is not compatible with
130 * the installed version of ToxAV.
143 */ 131 */
144void toxav_kill(ToxAv *av); 132#define TOXAV_VERSION_REQUIRE(MAJOR, MINOR, PATCH) \
133 typedef char toxav_required_version[TOXAV_IS_COMPATIBLE(MAJOR, MINOR, PATCH) ? 1 : -1]
145 134
146/** 135/**
147 * Returns the interval in milliseconds when the next toxav_do() should be called. 136 * A convenience macro to call toxav_version_is_compatible with the currently
148 * If no call is active at the moment returns 200. 137 * compiling API version.
149 */ 138 */
150uint32_t toxav_do_interval(ToxAv *av); 139#define TOXAV_VERSION_IS_ABI_COMPATIBLE() \
140 toxav_version_is_compatible(TOXAV_VERSION_MAJOR, TOXAV_VERSION_MINOR, TOXAV_VERSION_PATCH)
151 141
152/** 142/**
153 * Main loop for the session. Best called right after tox_do(); 143 * Return the major version number of the library. Can be used to display the
144 * ToxAV library version or to check whether the client is compatible with the
145 * dynamically linked version of ToxAV.
154 */ 146 */
155void toxav_do(ToxAv *av); 147uint32_t toxav_version_major(void);
156 148
157/** 149/**
158 * Register callback for call state. 150 * Return the minor version number of the library.
159 */ 151 */
160void toxav_register_callstate_callback (ToxAv *av, ToxAVCallback cb, ToxAvCallbackID id, void *userdata); 152uint32_t toxav_version_minor(void);
161 153
162/** 154/**
163 * Register callback for audio data. 155 * Return the patch number of the library.
164 */ 156 */
165void toxav_register_audio_callback (ToxAv *av, ToxAvAudioCallback cb, void *userdata); 157uint32_t toxav_version_patch(void);
166 158
167/** 159/**
168 * Register callback for video data. 160 * Return whether the compiled library version is compatible with the passed
161 * version numbers.
169 */ 162 */
170void toxav_register_video_callback (ToxAv *av, ToxAvVideoCallback cb, void *userdata); 163bool toxav_version_is_compatible(uint32_t major, uint32_t minor, uint32_t patch);
164
165
166/*******************************************************************************
167 *
168 * :: Creation and destruction
169 *
170 ******************************************************************************/
171typedef enum TOXAV_ERR_NEW {
172 /**
173 * The function returned successfully.
174 */
175 TOXAV_ERR_NEW_OK,
176 /**
177 * One of the arguments to the function was NULL when it was not expected.
178 */
179 TOXAV_ERR_NEW_NULL,
180 /**
181 * Memory allocation failure while trying to allocate structures required for
182 * the A/V session.
183 */
184 TOXAV_ERR_NEW_MALLOC,
185 /**
186 * Attempted to create a second session for the same Tox instance.
187 */
188 TOXAV_ERR_NEW_MULTIPLE,
189} TOXAV_ERR_NEW;
171 190
172/** 191/**
173 * Call user. Use its friend_id. 192 * Start new A/V session. There can only be only one session per Tox instance.
174 */ 193 */
175int toxav_call(ToxAv *av, 194ToxAV *toxav_new(Tox *tox, TOXAV_ERR_NEW *error);
176 int32_t *call_index,
177 int friend_id,
178 const ToxAvCSettings *csettings,
179 int ringing_seconds);
180 195
181/** 196/**
182 * Hangup active call. 197 * Releases all resources associated with the A/V session.
198 *
199 * If any calls were ongoing, these will be forcibly terminated without
200 * notifying peers. After calling this function, no other functions may be
201 * called and the av pointer becomes invalid.
183 */ 202 */
184int toxav_hangup(ToxAv *av, int32_t call_index); 203void toxav_kill(ToxAV *toxAV);
185 204
186/** 205/**
187 * Answer incoming call. Pass the csettings that you will use. 206 * Returns the Tox instance the A/V object was created for.
188 */ 207 */
189int toxav_answer(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings ); 208Tox *toxav_get_tox(const ToxAV *toxAV);
209
190 210
211/*******************************************************************************
212 *
213 * :: A/V event loop
214 *
215 ******************************************************************************/
191/** 216/**
192 * Reject incoming call. 217 * Returns the interval in milliseconds when the next toxav_iterate call should
218 * be. If no call is active at the moment, this function returns 200.
193 */ 219 */
194int toxav_reject(ToxAv *av, int32_t call_index, const char *reason); 220uint32_t toxav_iteration_interval(const ToxAV *toxAV);
195 221
196/** 222/**
197 * Cancel outgoing request. 223 * Main loop for the session. This function needs to be called in intervals of
224 * toxav_iteration_interval() milliseconds. It is best called in the separate
225 * thread from tox_iterate.
198 */ 226 */
199int toxav_cancel(ToxAv *av, int32_t call_index, int peer_id, const char *reason); 227void toxav_iterate(ToxAV *toxAV);
228
229
230/*******************************************************************************
231 *
232 * :: Call setup
233 *
234 ******************************************************************************/
235typedef enum TOXAV_ERR_CALL {
236 /**
237 * The function returned successfully.
238 */
239 TOXAV_ERR_CALL_OK,
240 /**
241 * A resource allocation error occurred while trying to create the structures
242 * required for the call.
243 */
244 TOXAV_ERR_CALL_MALLOC,
245 /**
246 * Synchronization error occurred.
247 */
248 TOXAV_ERR_CALL_SYNC,
249 /**
250 * The friend number did not designate a valid friend.
251 */
252 TOXAV_ERR_CALL_FRIEND_NOT_FOUND,
253 /**
254 * The friend was valid, but not currently connected.
255 */
256 TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED,
257 /**
258 * Attempted to call a friend while already in an audio or video call with
259 * them.
260 */
261 TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL,
262 /**
263 * Audio or video bit rate is invalid.
264 */
265 TOXAV_ERR_CALL_INVALID_BIT_RATE,
266} TOXAV_ERR_CALL;
200 267
201/** 268/**
202 * Notify peer that we are changing codec settings. 269 * Call a friend. This will start ringing the friend.
270 *
271 * It is the client's responsibility to stop ringing after a certain timeout,
272 * if such behaviour is desired. If the client does not stop ringing, the
273 * library will not stop until the friend is disconnected. Audio and video
274 * receiving are both enabled by default.
275 *
276 * @param friend_number The friend number of the friend that should be called.
277 * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
278 * audio sending.
279 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
280 * video sending.
203 */ 281 */
204int toxav_change_settings(ToxAv *av, int32_t call_index, const ToxAvCSettings *csettings); 282bool toxav_call(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate,
283 uint32_t video_bit_rate, TOXAV_ERR_CALL *error);
205 284
206/** 285/**
207 * Terminate transmission. Note that transmission will be 286 * The function type for the call callback.
208 * terminated without informing remote peer. Usually called when we can't inform peer. 287 *
288 * @param friend_number The friend number from which the call is incoming.
289 * @param audio_enabled True if friend is sending audio.
290 * @param video_enabled True if friend is sending video.
209 */ 291 */
210int toxav_stop_call(ToxAv *av, int32_t call_index); 292typedef void toxav_call_cb(ToxAV *toxAV, uint32_t friend_number, bool audio_enabled,
293 bool video_enabled, void *user_data);
211 294
212/** 295/**
213 * Allocates transmission data. Must be call before calling toxav_prepare_* and toxav_send_*. 296 * Set the callback for the `call` event. Pass NULL to unset.
214 * Also, it must be called when call is started 297 *
215 */ 298 */
216int toxav_prepare_transmission(ToxAv *av, int32_t call_index, int support_video); 299void toxav_callback_call(ToxAV *toxAV, toxav_call_cb *callback, void *user_data);
300
301typedef enum TOXAV_ERR_ANSWER {
302 /**
303 * The function returned successfully.
304 */
305 TOXAV_ERR_ANSWER_OK,
306 /**
307 * Synchronization error occurred.
308 */
309 TOXAV_ERR_ANSWER_SYNC,
310 /**
311 * Failed to initialize codecs for call session. Note that codec initiation
312 * will fail if there is no receive callback registered for either audio or
313 * video.
314 */
315 TOXAV_ERR_ANSWER_CODEC_INITIALIZATION,
316 /**
317 * The friend number did not designate a valid friend.
318 */
319 TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND,
320 /**
321 * The friend was valid, but they are not currently trying to initiate a call.
322 * This is also returned if this client is already in a call with the friend.
323 */
324 TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING,
325 /**
326 * Audio or video bit rate is invalid.
327 */
328 TOXAV_ERR_ANSWER_INVALID_BIT_RATE,
329} TOXAV_ERR_ANSWER;
217 330
218/** 331/**
219 * Clears transmission data. Call this at the end of the transmission. 332 * Accept an incoming call.
333 *
334 * If answering fails for any reason, the call will still be pending and it is
335 * possible to try and answer it later. Audio and video receiving are both
336 * enabled by default.
337 *
338 * @param friend_number The friend number of the friend that is calling.
339 * @param audio_bit_rate Audio bit rate in Kb/sec. Set this to 0 to disable
340 * audio sending.
341 * @param video_bit_rate Video bit rate in Kb/sec. Set this to 0 to disable
342 * video sending.
220 */ 343 */
221int toxav_kill_transmission(ToxAv *av, int32_t call_index); 344bool toxav_answer(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_ANSWER *error);
345
346
347/*******************************************************************************
348 *
349 * :: Call state graph
350 *
351 ******************************************************************************/
352enum TOXAV_FRIEND_CALL_STATE {
353 /**
354 * Set by the AV core if an error occurred on the remote end or if friend
355 * timed out. This is the final state after which no more state
356 * transitions can occur for the call. This call state will never be triggered
357 * in combination with other call states.
358 */
359 TOXAV_FRIEND_CALL_STATE_ERROR = 1,
360 /**
361 * The call has finished. This is the final state after which no more state
362 * transitions can occur for the call. This call state will never be
363 * triggered in combination with other call states.
364 */
365 TOXAV_FRIEND_CALL_STATE_FINISHED = 2,
366 /**
367 * The flag that marks that friend is sending audio.
368 */
369 TOXAV_FRIEND_CALL_STATE_SENDING_A = 4,
370 /**
371 * The flag that marks that friend is sending video.
372 */
373 TOXAV_FRIEND_CALL_STATE_SENDING_V = 8,
374 /**
375 * The flag that marks that friend is receiving audio.
376 */
377 TOXAV_FRIEND_CALL_STATE_ACCEPTING_A = 16,
378 /**
379 * The flag that marks that friend is receiving video.
380 */
381 TOXAV_FRIEND_CALL_STATE_ACCEPTING_V = 32,
382};
222 383
223/** 384/**
224 * Encode video frame. 385 * The function type for the call_state callback.
386 *
387 * @param friend_number The friend number for which the call state changed.
388 * @param state The bitmask of the new call state which is guaranteed to be
389 * different than the previous state. The state is set to 0 when the call is
390 * paused. The bitmask represents all the activities currently performed by the
391 * friend.
225 */ 392 */
226int toxav_prepare_video_frame ( ToxAv *av, 393typedef void toxav_call_state_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t state, void *user_data);
227 int32_t call_index,
228 uint8_t *dest,
229 int dest_max,
230 vpx_image_t *input);
231 394
232/** 395/**
233 * Send encoded video packet. 396 * Set the callback for the `call_state` event. Pass NULL to unset.
397 *
234 */ 398 */
235int toxav_send_video ( ToxAv *av, int32_t call_index, const uint8_t *frame, uint32_t frame_size); 399void toxav_callback_call_state(ToxAV *toxAV, toxav_call_state_cb *callback, void *user_data);
400
401/*******************************************************************************
402 *
403 * :: Call control
404 *
405 ******************************************************************************/
406typedef enum TOXAV_CALL_CONTROL {
407 /**
408 * Resume a previously paused call. Only valid if the pause was caused by this
409 * client, if not, this control is ignored. Not valid before the call is accepted.
410 */
411 TOXAV_CALL_CONTROL_RESUME,
412 /**
413 * Put a call on hold. Not valid before the call is accepted.
414 */
415 TOXAV_CALL_CONTROL_PAUSE,
416 /**
417 * Reject a call if it was not answered, yet. Cancel a call after it was
418 * answered.
419 */
420 TOXAV_CALL_CONTROL_CANCEL,
421 /**
422 * Request that the friend stops sending audio. Regardless of the friend's
423 * compliance, this will cause the audio_receive_frame event to stop being
424 * triggered on receiving an audio frame from the friend.
425 */
426 TOXAV_CALL_CONTROL_MUTE_AUDIO,
427 /**
428 * Calling this control will notify client to start sending audio again.
429 */
430 TOXAV_CALL_CONTROL_UNMUTE_AUDIO,
431 /**
432 * Request that the friend stops sending video. Regardless of the friend's
433 * compliance, this will cause the video_receive_frame event to stop being
434 * triggered on receiving a video frame from the friend.
435 */
436 TOXAV_CALL_CONTROL_HIDE_VIDEO,
437 /**
438 * Calling this control will notify client to start sending video again.
439 */
440 TOXAV_CALL_CONTROL_SHOW_VIDEO,
441} TOXAV_CALL_CONTROL;
442
443typedef enum TOXAV_ERR_CALL_CONTROL {
444 /**
445 * The function returned successfully.
446 */
447 TOXAV_ERR_CALL_CONTROL_OK,
448 /**
449 * Synchronization error occurred.
450 */
451 TOXAV_ERR_CALL_CONTROL_SYNC,
452 /**
453 * The friend_number passed did not designate a valid friend.
454 */
455 TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_FOUND,
456 /**
457 * This client is currently not in a call with the friend. Before the call is
458 * answered, only CANCEL is a valid control.
459 */
460 TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL,
461 /**
462 * Happens if user tried to pause an already paused call or if trying to
463 * resume a call that is not paused.
464 */
465 TOXAV_ERR_CALL_CONTROL_INVALID_TRANSITION,
466} TOXAV_ERR_CALL_CONTROL;
236 467
237/** 468/**
238 * Encode audio frame. 469 * Sends a call control command to a friend.
470 *
471 * @param friend_number The friend number of the friend this client is in a call
472 * with.
473 * @param control The control command to send.
474 *
475 * @return true on success.
239 */ 476 */
240int toxav_prepare_audio_frame ( ToxAv *av, 477bool toxav_call_control(ToxAV *toxAV, uint32_t friend_number, TOXAV_CALL_CONTROL control, TOXAV_ERR_CALL_CONTROL *error);
241 int32_t call_index, 478
242 uint8_t *dest, 479
243 int dest_max, 480/*******************************************************************************
244 const int16_t *frame, 481 *
245 int frame_size); 482 * :: Controlling bit rates
483 *
484 ******************************************************************************/
485typedef enum TOXAV_ERR_BIT_RATE_SET {
486 /**
487 * The function returned successfully.
488 */
489 TOXAV_ERR_BIT_RATE_SET_OK,
490 /**
491 * Synchronization error occurred.
492 */
493 TOXAV_ERR_BIT_RATE_SET_SYNC,
494 /**
495 * The audio bit rate passed was not one of the supported values.
496 */
497 TOXAV_ERR_BIT_RATE_SET_INVALID_AUDIO_BIT_RATE,
498 /**
499 * The video bit rate passed was not one of the supported values.
500 */
501 TOXAV_ERR_BIT_RATE_SET_INVALID_VIDEO_BIT_RATE,
502 /**
503 * The friend_number passed did not designate a valid friend.
504 */
505 TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND,
506 /**
507 * This client is currently not in a call with the friend.
508 */
509 TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL,
510} TOXAV_ERR_BIT_RATE_SET;
246 511
247/** 512/**
248 * Send encoded audio frame. 513 * Set the bit rate to be used in subsequent audio/video frames.
514 *
515 * @param friend_number The friend number of the friend for which to set the
516 * bit rate.
517 * @param audio_bit_rate The new audio bit rate in Kb/sec. Set to 0 to disable
518 * audio sending. Set to -1 to leave unchanged.
519 * @param video_bit_rate The new video bit rate in Kb/sec. Set to 0 to disable
520 * video sending. Set to -1 to leave unchanged.
521 *
249 */ 522 */
250int toxav_send_audio ( ToxAv *av, int32_t call_index, const uint8_t *frame, unsigned int size); 523bool toxav_bit_rate_set(ToxAV *toxAV, uint32_t friend_number, int32_t audio_bit_rate,
524 int32_t video_bit_rate, TOXAV_ERR_BIT_RATE_SET *error);
251 525
252/** 526/**
253 * Get codec settings from the peer. These were exchanged during call initialization 527 * The function type for the bit_rate_status callback. The event is triggered
254 * or when peer send us new csettings. 528 * when the network becomes too saturated for current bit rates at which
529 * point core suggests new bit rates.
530 *
531 * @param friend_number The friend number of the friend for which to set the
532 * bit rate.
533 * @param audio_bit_rate Suggested maximum audio bit rate in Kb/sec.
534 * @param video_bit_rate Suggested maximum video bit rate in Kb/sec.
255 */ 535 */
256int toxav_get_peer_csettings ( ToxAv *av, int32_t call_index, int peer, ToxAvCSettings *dest ); 536typedef void toxav_bit_rate_status_cb(ToxAV *toxAV, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, void *user_data);
257 537
258/** 538/**
259 * Get friend id of peer participating in conversation. 539 * Set the callback for the `bit_rate_status` event. Pass NULL to unset.
540 *
260 */ 541 */
261int toxav_get_peer_id ( ToxAv *av, int32_t call_index, int peer ); 542void toxav_callback_bit_rate_status(ToxAV *toxAV, toxav_bit_rate_status_cb *callback, void *user_data);
543
544
545/*******************************************************************************
546 *
547 * :: A/V sending
548 *
549 ******************************************************************************/
550typedef enum TOXAV_ERR_SEND_FRAME {
551 /**
552 * The function returned successfully.
553 */
554 TOXAV_ERR_SEND_FRAME_OK,
555 /**
556 * In case of video, one of Y, U, or V was NULL. In case of audio, the samples
557 * data pointer was NULL.
558 */
559 TOXAV_ERR_SEND_FRAME_NULL,
560 /**
561 * The friend_number passed did not designate a valid friend.
562 */
563 TOXAV_ERR_SEND_FRAME_FRIEND_NOT_FOUND,
564 /**
565 * This client is currently not in a call with the friend.
566 */
567 TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL,
568 /**
569 * Synchronization error occurred.
570 */
571 TOXAV_ERR_SEND_FRAME_SYNC,
572 /**
573 * One of the frame parameters was invalid. E.g. the resolution may be too
574 * small or too large, or the audio sampling rate may be unsupported.
575 */
576 TOXAV_ERR_SEND_FRAME_INVALID,
577 /**
578 * Either friend turned off audio or video receiving or we turned off sending
579 * for the said payload.
580 */
581 TOXAV_ERR_SEND_FRAME_PAYLOAD_TYPE_DISABLED,
582 /**
583 * Failed to push frame through rtp interface.
584 */
585 TOXAV_ERR_SEND_FRAME_RTP_FAILED,
586} TOXAV_ERR_SEND_FRAME;
262 587
263/** 588/**
264 * Get current call state. 589 * Send an audio frame to a friend.
265 */ 590 *
266ToxAvCallState toxav_get_call_state ( ToxAv *av, int32_t call_index ); 591 * The expected format of the PCM data is: [s1c1][s1c2][...][s2c1][s2c2][...]...
592 * Meaning: sample 1 for channel 1, sample 1 for channel 2, ...
593 * For mono audio, this has no meaning, every sample is subsequent. For stereo,
594 * this means the expected format is LRLRLR... with samples for left and right
595 * alternating.
596 *
597 * @param friend_number The friend number of the friend to which to send an
598 * audio frame.
599 * @param pcm An array of audio samples. The size of this array must be
600 * sample_count * channels.
601 * @param sample_count Number of samples in this frame. Valid numbers here are
602 * ((sample rate) * (audio length) / 1000), where audio length can be
603 * 2.5, 5, 10, 20, 40 or 60 millseconds.
604 * @param channels Number of audio channels. Supported values are 1 and 2.
605 * @param sampling_rate Audio sampling rate used in this frame. Valid sampling
606 * rates are 8000, 12000, 16000, 24000, or 48000.
607 */
608bool toxav_audio_send_frame(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
609 size_t sample_count, uint8_t channels, uint32_t sampling_rate,
610 TOXAV_ERR_SEND_FRAME *error);
267 611
268/** 612/**
269 * Is certain capability supported. Used to determine if encoding/decoding is ready. 613 * Send a video frame to a friend.
614 *
615 * Y - plane should be of size: height * width
616 * U - plane should be of size: (height/2) * (width/2)
617 * V - plane should be of size: (height/2) * (width/2)
618 *
619 * @param friend_number The friend number of the friend to which to send a video
620 * frame.
621 * @param width Width of the frame in pixels.
622 * @param height Height of the frame in pixels.
623 * @param y Y (Luminance) plane data.
624 * @param u U (Chroma) plane data.
625 * @param v V (Chroma) plane data.
626 */
627bool toxav_video_send_frame(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
628 uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
629 TOXAV_ERR_SEND_FRAME *error);
630
631
632/*******************************************************************************
633 *
634 * :: A/V receiving
635 *
636 ******************************************************************************/
637/**
638 * The function type for the audio_receive_frame callback. The callback can be
639 * called multiple times per single iteration depending on the amount of queued
640 * frames in the buffer. The received format is the same as in send function.
641 *
642 * @param friend_number The friend number of the friend who sent an audio frame.
643 * @param pcm An array of audio samples (sample_count * channels elements).
644 * @param sample_count The number of audio samples per channel in the PCM array.
645 * @param channels Number of audio channels.
646 * @param sampling_rate Sampling rate used in this frame.
647 *
270 */ 648 */
271int toxav_capability_supported ( ToxAv *av, int32_t call_index, ToxAvCapabilities capability ); 649typedef void toxav_audio_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, const int16_t *pcm,
650 size_t sample_count, uint8_t channels, uint32_t sampling_rate,
651 void *user_data);
272 652
273/** 653/**
274 * Returns tox reference. 654 * Set the callback for the `audio_receive_frame` event. Pass NULL to unset.
655 *
275 */ 656 */
276Tox *toxav_get_tox (ToxAv *av); 657void toxav_callback_audio_receive_frame(ToxAV *toxAV, toxav_audio_receive_frame_cb *callback, void *user_data);
658
659/**
660 * The function type for the video_receive_frame callback.
661 *
662 * @param friend_number The friend number of the friend who sent a video frame.
663 * @param width Width of the frame in pixels.
664 * @param height Height of the frame in pixels.
665 * @param y
666 * @param u
667 * @param v Plane data.
668 * The size of plane data is derived from width and height where
669 * Y = MAX(width, abs(ystride)) * height,
670 * U = MAX(width/2, abs(ustride)) * (height/2) and
671 * V = MAX(width/2, abs(vstride)) * (height/2).
672 * @param ystride
673 * @param ustride
674 * @param vstride Strides data. Strides represent padding for each plane
675 * that may or may not be present. You must handle strides in
676 * your image processing code. Strides are negative if the
677 * image is bottom-up hence why you MUST abs() it when
678 * calculating plane buffer size.
679 */
680typedef void toxav_video_receive_frame_cb(ToxAV *toxAV, uint32_t friend_number, uint16_t width,
681 uint16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
682 int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
277 683
278/** 684/**
279 * Returns number of active calls or -1 on error. 685 * Set the callback for the `video_receive_frame` event. Pass NULL to unset.
686 *
280 */ 687 */
281int toxav_get_active_count (ToxAv *av); 688void toxav_callback_video_receive_frame(ToxAV *toxAV, toxav_video_receive_frame_cb *callback, void *user_data);
282 689
690/**
691 * NOTE Compatibility with old toxav group calls TODO remove
692 */
283/* Create a new toxav group. 693/* Create a new toxav group.
284 * 694 *
285 * return group number on success. 695 * return group number on success.
@@ -290,7 +700,7 @@ int toxav_get_active_count (ToxAv *av);
290 * 700 *
291 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 701 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
292 */ 702 */
293int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, 703int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(void*, int, int, const int16_t *, unsigned int, uint8_t,
294 unsigned int, void *), void *userdata); 704 unsigned int, void *), void *userdata);
295 705
296/* Join a AV group (you need to have been invited first.) 706/* Join a AV group (you need to have been invited first.)
@@ -304,7 +714,7 @@ int toxav_add_av_groupchat(Tox *tox, void (*audio_callback)(Tox *, int, int, con
304 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)). 714 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
305 */ 715 */
306int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length, 716int toxav_join_av_groupchat(Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length,
307 void (*audio_callback)(Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata); 717 void (*audio_callback)(void*, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *), void *userdata);
308 718
309/* Send audio to the group chat. 719/* Send audio to the group chat.
310 * 720 *
@@ -325,5 +735,4 @@ int toxav_group_send_audio(Tox *tox, int groupnumber, const int16_t *pcm, unsign
325#ifdef __cplusplus 735#ifdef __cplusplus
326} 736}
327#endif 737#endif
328 738#endif /* TOXAV_H */
329#endif /* __TOXAV */
diff --git a/toxav/toxav_old.c b/toxav/toxav_old.c
new file mode 100644
index 00000000..7d7e5e7b
--- /dev/null
+++ b/toxav/toxav_old.c
@@ -0,0 +1,81 @@
1/* toxav_old.h
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 */
21/**
22 * This file contains the group chats code for the backwards compatibility.
23 */
24
25#include "toxav.h"
26#include "group.h"
27
28/* Create a new toxav group.
29 *
30 * return group number on success.
31 * return -1 on failure.
32 *
33 * Audio data callback format:
34 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
35 *
36 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
37 */
38int toxav_add_av_groupchat(struct Tox *tox, void (*audio_callback)(void *, int, int, const int16_t *, unsigned int,
39 uint8_t, unsigned int, void *), void *userdata)
40{
41 Messenger *m = (Messenger *)tox;
42 return add_av_groupchat(m->group_chat_object, audio_callback, userdata);
43}
44
45/* Join a AV group (you need to have been invited first.)
46 *
47 * returns group number on success
48 * returns -1 on failure.
49 *
50 * Audio data callback format (same as the one for toxav_add_av_groupchat()):
51 * audio_callback(Tox *tox, int groupnumber, int peernumber, const int16_t *pcm, unsigned int samples, uint8_t channels, unsigned int sample_rate, void *userdata)
52 *
53 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
54 */
55int toxav_join_av_groupchat(struct Tox *tox, int32_t friendnumber, const uint8_t *data, uint16_t length,
56 void (*audio_callback)(void *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int, void *),
57 void *userdata)
58{
59 Messenger *m = (Messenger *)tox;
60 return join_av_groupchat(m->group_chat_object, friendnumber, data, length, audio_callback, userdata);
61}
62
63/* Send audio to the group chat.
64 *
65 * return 0 on success.
66 * return -1 on failure.
67 *
68 * Note that total size of pcm in bytes is equal to (samples * channels * sizeof(int16_t)).
69 *
70 * Valid number of samples are ((sample rate) * (audio length (Valid ones are: 2.5, 5, 10, 20, 40 or 60 ms)) / 1000)
71 * Valid number of channels are 1 or 2.
72 * Valid sample rates are 8000, 12000, 16000, 24000, or 48000.
73 *
74 * Recommended values are: samples = 960, channels = 1, sample_rate = 48000
75 */
76int toxav_group_send_audio(struct Tox *tox, int groupnumber, const int16_t *pcm, unsigned int samples, uint8_t channels,
77 unsigned int sample_rate)
78{
79 Messenger *m = (Messenger *)tox;
80 return group_send_audio(m->group_chat_object, groupnumber, pcm, samples, channels, sample_rate);
81} \ No newline at end of file
diff --git a/toxav/video.c b/toxav/video.c
new file mode 100644
index 00000000..acc1852b
--- /dev/null
+++ b/toxav/video.c
@@ -0,0 +1,267 @@
1/** video.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 */
21
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif /* HAVE_CONFIG_H */
25
26#include <stdlib.h>
27#include <assert.h>
28
29#include "video.h"
30#include "msi.h"
31#include "rtp.h"
32
33#include "../toxcore/logger.h"
34#include "../toxcore/network.h"
35
36#define MAX_DECODE_TIME_US 0 /* Good quality encode. */
37#define VIDEO_DECODE_BUFFER_SIZE 20
38
39VCSession *vc_new(ToxAV *av, uint32_t friend_number, toxav_video_receive_frame_cb *cb, void *cb_data)
40{
41 VCSession *vc = calloc(sizeof(VCSession), 1);
42
43 if (!vc) {
44 LOGGER_WARNING("Allocation failed! Application might misbehave!");
45 return NULL;
46 }
47
48 if (create_recursive_mutex(vc->queue_mutex) != 0) {
49 LOGGER_WARNING("Failed to create recursive mutex!");
50 free(vc);
51 return NULL;
52 }
53
54 if (!(vc->vbuf_raw = rb_new(VIDEO_DECODE_BUFFER_SIZE)))
55 goto BASE_CLEANUP;
56
57 int rc = vpx_codec_dec_init(vc->decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0);
58
59 if (rc != VPX_CODEC_OK) {
60 LOGGER_ERROR("Init video_decoder failed: %s", vpx_codec_err_to_string(rc));
61 goto BASE_CLEANUP;
62 }
63
64 /* Set encoder to some initial values
65 */
66 vpx_codec_enc_cfg_t cfg;
67 rc = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
68
69 if (rc != VPX_CODEC_OK) {
70 LOGGER_ERROR("Failed to get config: %s", vpx_codec_err_to_string(rc));
71 goto BASE_CLEANUP_1;
72 }
73
74 cfg.rc_target_bitrate = 500000;
75 cfg.g_w = 800;
76 cfg.g_h = 600;
77 cfg.g_pass = VPX_RC_ONE_PASS;
78 /* FIXME If we set error resilience the app will crash due to bug in vp8.
79 Perhaps vp9 has solved it?*/
80// cfg.g_error_resilient = VPX_ERROR_RESILIENT_DEFAULT | VPX_ERROR_RESILIENT_PARTITIONS;
81 cfg.g_lag_in_frames = 0;
82 cfg.kf_min_dist = 0;
83 cfg.kf_max_dist = 48;
84 cfg.kf_mode = VPX_KF_AUTO;
85
86 rc = vpx_codec_enc_init(vc->encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
87
88 if (rc != VPX_CODEC_OK) {
89 LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
90 goto BASE_CLEANUP_1;
91 }
92
93 rc = vpx_codec_control(vc->encoder, VP8E_SET_CPUUSED, 8);
94
95 if (rc != VPX_CODEC_OK) {
96 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
97 vpx_codec_destroy(vc->encoder);
98 goto BASE_CLEANUP_1;
99 }
100
101 vc->linfts = current_time_monotonic();
102 vc->lcfd = 60;
103 vc->vcb.first = cb;
104 vc->vcb.second = cb_data;
105 vc->friend_number = friend_number;
106 vc->av = av;
107
108 return vc;
109
110BASE_CLEANUP_1:
111 vpx_codec_destroy(vc->decoder);
112BASE_CLEANUP:
113 pthread_mutex_destroy(vc->queue_mutex);
114 rb_kill(vc->vbuf_raw);
115 free(vc);
116 return NULL;
117}
118void vc_kill(VCSession *vc)
119{
120 if (!vc)
121 return;
122
123 vpx_codec_destroy(vc->encoder);
124 vpx_codec_destroy(vc->decoder);
125
126 void *p;
127
128 while (rb_read(vc->vbuf_raw, (void **)&p))
129 free(p);
130
131 rb_kill(vc->vbuf_raw);
132
133 pthread_mutex_destroy(vc->queue_mutex);
134
135 LOGGER_DEBUG("Terminated video handler: %p", vc);
136 free(vc);
137}
138void vc_iterate(VCSession *vc)
139{
140 if (!vc)
141 return;
142
143 struct RTPMessage *p;
144 int rc;
145
146 pthread_mutex_lock(vc->queue_mutex);
147
148 if (rb_read(vc->vbuf_raw, (void **)&p)) {
149 pthread_mutex_unlock(vc->queue_mutex);
150
151 rc = vpx_codec_decode(vc->decoder, p->data, p->len, NULL, MAX_DECODE_TIME_US);
152 free(p);
153
154 if (rc != VPX_CODEC_OK)
155 LOGGER_ERROR("Error decoding video: %s", vpx_codec_err_to_string(rc));
156 else {
157 vpx_codec_iter_t iter = NULL;
158 vpx_image_t *dest = vpx_codec_get_frame(vc->decoder, &iter);
159
160 /* Play decoded images */
161 for (; dest; dest = vpx_codec_get_frame(vc->decoder, &iter)) {
162 if (vc->vcb.first)
163 vc->vcb.first(vc->av, vc->friend_number, dest->d_w, dest->d_h,
164 (const uint8_t *)dest->planes[0], (const uint8_t *)dest->planes[1], (const uint8_t *)dest->planes[2],
165 dest->stride[0], dest->stride[1], dest->stride[2], vc->vcb.second);
166
167 vpx_img_free(dest);
168 }
169 }
170
171 return;
172 }
173
174 pthread_mutex_unlock(vc->queue_mutex);
175}
176int vc_queue_message(void *vcp, struct RTPMessage *msg)
177{
178 /* This function does the reconstruction of video packets.
179 * See more info about video splitting in docs
180 */
181 if (!vcp || !msg)
182 return -1;
183
184 if (msg->header.pt == (rtp_TypeVideo + 2) % 128) {
185 LOGGER_WARNING("Got dummy!");
186 free(msg);
187 return 0;
188 }
189
190 if (msg->header.pt != rtp_TypeVideo % 128) {
191 LOGGER_WARNING("Invalid payload type!");
192 free(msg);
193 return -1;
194 }
195
196 VCSession *vc = vcp;
197
198 pthread_mutex_lock(vc->queue_mutex);
199 free(rb_write(vc->vbuf_raw, msg));
200 {
201 /* Calculate time took for peer to send us this frame */
202 uint32_t t_lcfd = current_time_monotonic() - vc->linfts;
203 vc->lcfd = t_lcfd > 100 ? vc->lcfd : t_lcfd;
204 vc->linfts = current_time_monotonic();
205 }
206 pthread_mutex_unlock(vc->queue_mutex);
207
208 return 0;
209}
210int vc_reconfigure_encoder(VCSession* vc, uint32_t bit_rate, uint16_t width, uint16_t height)
211{
212 if (!vc)
213 return -1;
214
215 vpx_codec_enc_cfg_t cfg = *vc->encoder->config.enc;
216 int rc;
217
218 if (cfg.rc_target_bitrate == bit_rate && cfg.g_w == width && cfg.g_h == height)
219 return 0; /* Nothing changed */
220
221 if (cfg.g_w == width && cfg.g_h == height)
222 {
223 /* Only bit rate changed */
224 cfg.rc_target_bitrate = bit_rate;
225
226 rc = vpx_codec_enc_config_set(vc->encoder, &cfg);
227
228 if (rc != VPX_CODEC_OK) {
229 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
230 return -1;
231 }
232 }
233 else
234 {
235 /* Resolution is changed, must reinitialize encoder since libvpx v1.4 doesn't support
236 * reconfiguring encoder to use resolutions greater than initially set.
237 */
238
239 LOGGER_DEBUG("Have to reinitialize vpx encoder on session %p", vc);
240
241 cfg.rc_target_bitrate = bit_rate;
242 cfg.g_w = width;
243 cfg.g_h = height;
244
245 vpx_codec_ctx_t new_c;
246
247 rc = vpx_codec_enc_init(&new_c, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
248
249 if (rc != VPX_CODEC_OK) {
250 LOGGER_ERROR("Failed to initialize encoder: %s", vpx_codec_err_to_string(rc));
251 return -1;
252 }
253
254 rc = vpx_codec_control(&new_c, VP8E_SET_CPUUSED, 8);
255
256 if (rc != VPX_CODEC_OK) {
257 LOGGER_ERROR("Failed to set encoder control setting: %s", vpx_codec_err_to_string(rc));
258 vpx_codec_destroy(&new_c);
259 return -1;
260 }
261
262 vpx_codec_destroy(vc->encoder);
263 memcpy(vc->encoder, &new_c, sizeof(new_c));
264 }
265
266 return 0;
267}
diff --git a/toxav/video.h b/toxav/video.h
new file mode 100644
index 00000000..51f34318
--- /dev/null
+++ b/toxav/video.h
@@ -0,0 +1,67 @@
1/** video.h
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 */
21
22#ifndef VIDEO_H
23#define VIDEO_H
24
25#include <vpx/vpx_decoder.h>
26#include <vpx/vpx_encoder.h>
27#include <vpx/vp8dx.h>
28#include <vpx/vp8cx.h>
29#include <vpx/vpx_image.h>
30#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx())
31#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
32
33#include <pthread.h>
34
35#include "toxav.h"
36
37#include "../toxcore/util.h"
38
39struct RTPMessage;
40
41typedef struct VCSession_s {
42 /* encoding */
43 vpx_codec_ctx_t encoder[1];
44 uint32_t frame_counter;
45
46 /* decoding */
47 vpx_codec_ctx_t decoder[1];
48 void *vbuf_raw; /* Un-decoded data */
49
50 uint64_t linfts; /* Last received frame time stamp */
51 uint32_t lcfd; /* Last calculated frame duration for incoming video payload */
52
53 ToxAV *av;
54 uint32_t friend_number;
55
56 PAIR(toxav_video_receive_frame_cb *, void *) vcb; /* Video frame receive callback */
57
58 pthread_mutex_t queue_mutex[1];
59} VCSession;
60
61VCSession *vc_new(ToxAV* av, uint32_t friend_number, toxav_video_receive_frame_cb* cb, void* cb_data);
62void vc_kill(VCSession *vc);
63void vc_iterate(VCSession *vc);
64int vc_queue_message(void *vcp, struct RTPMessage *msg);
65int vc_reconfigure_encoder(VCSession *vc, uint32_t bit_rate, uint16_t width, uint16_t height);
66
67#endif /* VIDEO_H */