summaryrefslogtreecommitdiff
path: root/toxav
diff options
context:
space:
mode:
authorirungentoo <irungentoo@gmail.com>2014-02-16 19:13:34 -0500
committerirungentoo <irungentoo@gmail.com>2014-02-16 19:13:34 -0500
commit9d1eb27717472f5655242fac7b8123a44f1ff33c (patch)
treefd205f1ea53749717d9930d93ddb153fc8a16929 /toxav
parentfd3dc22ef3bd77beb78faf24cd43e9d2b9706f5d (diff)
parentf79b327fd639b2dcca9d4d2c137f93c1dce622fc (diff)
Merge branch 'av-fix' into av-good
Diffstat (limited to 'toxav')
-rw-r--r--toxav/Makefile.inc75
-rwxr-xr-xtoxav/event.c373
-rwxr-xr-xtoxav/event.h50
-rw-r--r--toxav/media.c309
-rw-r--r--toxav/media.h80
-rw-r--r--toxav/msi.c1379
-rw-r--r--toxav/msi.h233
-rwxr-xr-xtoxav/phone.c1308
-rw-r--r--toxav/rtp.c908
-rw-r--r--toxav/rtp.h219
-rw-r--r--toxav/toxav.c539
-rw-r--r--toxav/toxav.h300
12 files changed, 5773 insertions, 0 deletions
diff --git a/toxav/Makefile.inc b/toxav/Makefile.inc
new file mode 100644
index 00000000..30879f65
--- /dev/null
+++ b/toxav/Makefile.inc
@@ -0,0 +1,75 @@
1if BUILD_AV
2
3lib_LTLIBRARIES += libtoxav.la
4libtoxav_la_include_HEADERS = ../toxav/toxav.h
5libtoxav_la_includedir = $(includedir)/tox
6
7libtoxav_la_SOURCES = ../toxav/event.h \
8 ../toxav/event.c \
9 ../toxav/rtp.h \
10 ../toxav/rtp.c \
11 ../toxav/msi.h \
12 ../toxav/msi.c \
13 ../toxav/media.h \
14 ../toxav/media.c \
15 ../toxav/toxav.h \
16 ../toxav/toxav.c
17
18
19libtoxav_la_CFLAGS = -I../toxcore \
20 -I../toxav \
21 $(NACL_CFLAGS) \
22 $(OPUS_CFLAGS) \
23 $(VPX_CFLAGS) \
24 $(PTHREAD_CFLAGS)
25
26libtoxav_la_LDFLAGS = $(TOXAV_LT_LDFLAGS) \
27 $(NACL_LDFLAGS) \
28 $(EXTRA_LT_LDFLAGS)
29
30libtoxav_la_LIBS = $(NACL_LIBS) \
31 $(OPUS_LIBS) \
32 $(VPX_LIBS) \
33 $(PTHREAD_LIBS)
34
35
36endif
37
38
39
40
41
42
43if BUILD_PHONE
44
45
46noinst_PROGRAMS += phone
47
48phone_SOURCES = ../toxav/phone.c
49
50phone_CFLAGS = -I../toxcore \
51 -I../toxav \
52 $(AVFORMAT_CFLAGS) \
53 $(AVCODEC_CFLAGS) \
54 $(AVUTIL_CFLAGS) \
55 $(AVDEVICE_CFLAGS) \
56 $(SWSCALE_CFLAGS) \
57 $(SDL_CFLAGS) \
58 $(OPENAL_CFLAGS)
59
60phone_LDADD = libtoxav.la \
61 libtoxcore.la \
62 $(AVFORMAT_LIBS) \
63 $(AVCODEC_LIBS) \
64 $(AVUTIL_LIBS) \
65 $(AVDEVICE_LIBS) \
66 $(SWSCALE_LIBS) \
67 $(SDL_LIBS) \
68 $(OPENAL_LIBS) \
69 $(OPUS_LIBS) \
70 $(VPX_LIBS)\
71 $(PTHREAD_LIBS)\
72 $(NACL_LIBS)
73
74
75endif \ No newline at end of file
diff --git a/toxav/event.c b/toxav/event.c
new file mode 100755
index 00000000..9efe4be4
--- /dev/null
+++ b/toxav/event.c
@@ -0,0 +1,373 @@
1/** event.c
2 *
3 * Copyright (C) 2013 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 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif /* HAVE_CONFIG_H */
28
29#include <stdlib.h>
30#include "event.h"
31
32#include "../toxcore/util.h"
33#include "../toxcore/network.h"
34
35#define _GNU_SOURCE
36
37#include <assert.h>
38#include <unistd.h>
39#include <stddef.h>
40#include <inttypes.h>
41#include <pthread.h>
42#include <stdio.h>
43
44#define RUN_IN_THREAD(func, args) { pthread_t _tid; \
45pthread_create(&_tid, NULL, func, args); assert( pthread_detach(_tid) == 0 ); }
46
47#define LOCK(event_handler) pthread_mutex_lock (&event_handler->mutex)
48#define UNLOCK(event_handler) pthread_mutex_unlock(&event_handler->mutex)
49
50#define FREQUENCY 10000
51
52#define inline__ inline __attribute__((always_inline))
53
54
55typedef struct _EventContainer {
56 void* (*func)(void*);
57 void* func_args;
58 unsigned timeout;
59 long long id;
60
61} EventContainer;
62
63typedef struct _EventHandler {
64 EventContainer* timed_events;
65 size_t timed_events_count;
66
67 int running;
68
69 pthread_mutex_t mutex;
70
71} EventHandler;
72
73int throw_event( void* (func)(void*), void* arg );
74int reset_timer_event ( int id, uint32_t timeout );
75int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout);
76int cancel_timer_event ( int id );
77int execute_timer_event ( int id );
78
79struct _Event event =
80{
81 throw_event,
82 /* reset_timer_event */ NULL,
83 throw_timer_event,
84 cancel_timer_event,
85 /*execute_timer_event*/ NULL
86};
87
88/*
89 * Random functions used by this file
90 */
91void clear_events (EventContainer** event_container, size_t* counter)
92{
93 free(*event_container );
94
95 *event_container = NULL;
96 *counter = 0;
97}
98
99int pop_id ( EventContainer** event_container, size_t* counter, int id )
100{
101 if ( !*event_container || !*counter || !id )
102 return -1;
103
104 EventContainer* _it = *event_container;
105 int i;
106
107 for ( i = *counter; i; -- i ){
108 if ( _it->id == id ) { /* Hit! */
109 break;
110 }
111 ++_it;
112 }
113
114 if ( i ) {
115 for ( ; i; -- i ){ *_it = *(_it + 1); ++_it; }
116 -- (*counter );
117
118 if ( !(*counter)) { /* Free and set to NULL */
119 free(*event_container);
120 *event_container = NULL;
121 }
122 else {
123 void* _result = realloc(*event_container, sizeof(EventContainer) * (*counter )); /* resize */
124
125
126 if ( _result != NULL ) { *event_container = _result; return 0; }
127 else {
128 /* Not sure what would happen next so abort execution.
129 */
130 fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__);
131 abort();
132 return -1;
133 }
134 }
135 }
136
137 /* not found here */
138
139 return -1;
140}
141
142void push_event ( EventContainer** container, size_t* counter, void* (func)(void*), void* arg )
143{
144 EventContainer* _new = realloc((*container ), sizeof(EventContainer) * ((*counter ) + 1));
145
146 if ( _new == NULL ) {
147 /* Not sure what would happen next so abort execution.
148 * TODO: This could notice the calling function
149 * about realloc failing.
150 */
151 fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__);
152 abort();
153 }
154
155 _new[*counter].func = func;
156 _new[*counter].func_args = arg;
157 _new[*counter].timeout = 0;
158 _new[*counter].id = 0;
159
160 (*container) = _new;
161
162 (*counter )++;
163}
164
165void reorder_events ( size_t counter, EventContainer* container, unsigned timeout )
166{
167 if ( counter > 1 ) {
168
169 int i = counter - 1;
170
171 /* start from behind excluding last added member */
172 EventContainer* _it = &container[i - 1];
173
174 EventContainer _last_added = container[i];
175
176 for ( ; i; --i ) {
177 if ( _it->timeout > timeout ){
178 *(_it + 1) = *_it;
179 *_it = _last_added; -- _it;
180 }
181 }
182
183 }
184}
185
186/* ============================================= */
187
188/* main poll for event execution */
189void* event_poll( void* arg )
190{
191 EventHandler* _event_handler = arg;
192
193 while ( _event_handler->running )
194 {
195
196 LOCK( _event_handler );
197
198 if ( _event_handler->timed_events ){
199
200 uint32_t _time = ((uint32_t)(current_time() / 1000));
201
202 if ( _event_handler->timed_events[0].timeout < _time ) {
203
204 RUN_IN_THREAD ( _event_handler->timed_events[0].func,
205 _event_handler->timed_events[0].func_args );
206
207 pop_id(&_event_handler->timed_events,
208 &_event_handler->timed_events_count,
209 _event_handler->timed_events[0].id);
210
211 }
212
213 }
214
215 UNLOCK( _event_handler );
216
217 usleep(FREQUENCY);
218 }
219
220 LOCK( _event_handler );
221
222 clear_events(&_event_handler->timed_events, &_event_handler->timed_events_count);
223
224 UNLOCK( _event_handler );
225
226 _event_handler->running = -1;
227 pthread_exit(NULL);
228}
229
230int throw_event( void* (func)(void*), void* arg )
231{
232 pthread_t _tid;
233 int _rc =
234 pthread_create(&_tid, NULL, func, arg );
235
236 return (0 != _rc ) ? _rc : pthread_detach(_tid);
237}
238
239EventHandler event_handler;
240
241/* Place and order array of timers */
242int throw_timer_event ( void* (func)(void*), void* arg, unsigned timeout)
243{
244 static int _unique_id = 1;
245
246 push_event(&event_handler.timed_events, &(event_handler.timed_events_count), func, arg );
247
248 size_t _counter = event_handler.timed_events_count;
249
250 event_handler.timed_events[_counter - 1].timeout = timeout + ((uint32_t)(current_time() / 1000));
251 event_handler.timed_events[_counter - 1].id = _unique_id; ++_unique_id;
252
253
254 /* reorder */
255
256 reorder_events(_counter, event_handler.timed_events, timeout );
257
258 return _unique_id - 1;
259}
260
261int execute_timer_event ( int id )
262{
263 int _status;
264
265 LOCK((&event_handler));
266 EventContainer* _it = event_handler.timed_events;
267
268 int _i = event_handler.timed_events_count;
269
270 /* Find it and execute */
271 for ( ; _i; _i-- ) {
272 if ( _it->id == id ) {
273 RUN_IN_THREAD ( _it->func, _it->func_args );
274 break;
275 }
276 ++_it;
277 }
278
279 /* Now remove it from the queue */
280
281 if ( _i ) {
282 for ( ; _i; -- _i ){ *_it = *(_it + 1); ++_it; }
283
284 -- event_handler.timed_events_count;
285
286 if ( !event_handler.timed_events_count ) { /* Free and set to null */
287 free(event_handler.timed_events);
288 event_handler.timed_events = NULL;
289 }
290 else {
291 void* _result = realloc(event_handler.timed_events, sizeof(EventContainer) * event_handler.timed_events_count); /* resize */
292
293 if ( _result != NULL ) { event_handler.timed_events = _result; }
294 else {
295 /* Not sure what would happen next so abort execution.
296 */
297 fprintf(stderr, "CRITICAL! Failed to reallocate memory in %s():%d, aborting...", __func__, __LINE__);
298 abort();
299 return -1;
300 }
301 }
302
303 _status = 0;
304
305 }
306 else _status = -1;
307
308 UNLOCK((&event_handler));
309
310 return _status;
311}
312
313int reset_timer_event ( int id, uint32_t timeout )
314{
315 int _status;
316
317 LOCK((&event_handler));
318
319 EventContainer* _it = event_handler.timed_events;
320
321 int _i = event_handler.timed_events_count;
322
323 /* Find it and change */
324 for ( ; _i; _i-- ) {
325 if ( _it->id == id ) {
326 _it->timeout = timeout + ((uint32_t)(current_time() / 1000));
327 break;
328 }
329 ++_it;
330 }
331
332 _status = _i ? -1 : 0;
333
334 UNLOCK((&event_handler));
335
336 return _status;
337}
338
339/* Remove timer from array */
340inline__ int cancel_timer_event ( int id )
341{
342 return pop_id (&event_handler.timed_events, &event_handler.timed_events_count, id );
343}
344
345
346/* Initialization and termination of event polls
347 * This will be run at the beginning and the end of the program execution.
348 * I think that's the best way to do it.
349 */
350
351void __attribute__((constructor)) init_event_poll ()
352{
353 event_handler.timed_events = NULL;
354 event_handler.timed_events_count = 0;
355
356 event_handler.running = 1;
357
358 pthread_mutex_init(&event_handler.mutex, NULL);
359
360 RUN_IN_THREAD(event_poll, &event_handler);
361}
362
363/* NOTE: Do we need this? */
364void __attribute__((destructor)) terminate_event_poll()
365{
366 /* Exit thread */
367 event_handler.running = 0;
368
369 /* Give it enought time to exit */
370 usleep(FREQUENCY*2);
371
372 pthread_mutex_destroy( &event_handler.mutex );
373} \ No newline at end of file
diff --git a/toxav/event.h b/toxav/event.h
new file mode 100755
index 00000000..17dadbd5
--- /dev/null
+++ b/toxav/event.h
@@ -0,0 +1,50 @@
1/** event.h
2 *
3 * Copyright (C) 2013 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 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24
25#ifndef __TOXEVENT
26#define __TOXEVENT
27
28
29/**
30 * - Events are, in fact, ran in their own threads upon execution.
31 * - Event handler is initialized at the start, before the main() function
32 * and terminated after it's execution.
33 * - Timers are checked for timeout every ~10000 ns.
34 * - Timers can be canceled or ran immediately via
35 * timer_release() or timer_now() functions.
36 * - Timeout is measured in milliseconds.
37 *
38 * NOTE: timer_reset () and timer_now() are not tested nor usable atm
39 *
40 */
41extern struct _Event
42{
43 int (*rise) (void* ( func ) ( void* ), void* arg);
44 int (*timer_reset ) ( int id, unsigned timeout );
45 int (*timer_alloc) (void* ( func ) ( void* ), void* arg, unsigned timeout);
46 int (*timer_release) (int id);
47 int (*timer_now) ( int id );
48} event;
49
50#endif /* _MSI__EVENT_H_ */
diff --git a/toxav/media.c b/toxav/media.c
new file mode 100644
index 00000000..c4894076
--- /dev/null
+++ b/toxav/media.c
@@ -0,0 +1,309 @@
1/** media.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 <stdio.h>
30#include <stdlib.h>
31#include <math.h>
32#include <assert.h>
33
34#include "rtp.h"
35#include "media.h"
36
37struct jitter_buffer {
38 RTPMessage **queue;
39 uint16_t capacity;
40 uint16_t size;
41 uint16_t front;
42 uint16_t rear;
43 uint8_t queue_ready;
44 uint16_t current_id;
45 uint32_t current_ts;
46 uint8_t id_set;
47};
48
49
50struct jitter_buffer *create_queue(int capacity)
51{
52 struct jitter_buffer *q;
53 q = (struct jitter_buffer *)calloc(sizeof(struct jitter_buffer),1);
54 q->queue = (RTPMessage **)calloc(sizeof(RTPMessage*), capacity);
55 int i = 0;
56
57 for (i = 0; i < capacity; ++i) {
58 q->queue[i] = NULL;
59 }
60
61 q->size = 0;
62 q->capacity = capacity;
63 q->front = 0;
64 q->rear = -1;
65 q->queue_ready = 0;
66 q->current_id = 0;
67 q->current_ts = 0;
68 q->id_set = 0;
69 return q;
70}
71
72/* returns 1 if 'a' has a higher sequence number than 'b' */
73uint8_t sequence_number_older(uint16_t sn_a, uint16_t sn_b, uint32_t ts_a, uint32_t ts_b)
74{
75 /* TODO: There is already this kind of function in toxrtp.c.
76 * Maybe merge?
77 */
78 return (sn_a > sn_b || ts_a > ts_b);
79}
80
81/* success is 0 when there is nothing to dequeue, 1 when there's a good packet, 2 when there's a lost packet */
82RTPMessage *dequeue(struct jitter_buffer *q, int *success)
83{
84 if (q->size == 0 || q->queue_ready == 0) {
85 q->queue_ready = 0;
86 *success = 0;
87 return NULL;
88 }
89
90 int front = q->front;
91
92 if (q->id_set == 0) {
93 q->current_id = q->queue[front]->header->sequnum;
94 q->current_ts = q->queue[front]->header->timestamp;
95 q->id_set = 1;
96 } else {
97 int next_id = q->queue[front]->header->sequnum;
98 int next_ts = q->queue[front]->header->timestamp;
99
100 /* if this packet is indeed the expected packet */
101 if (next_id == (q->current_id + 1) % MAX_SEQU_NUM) {
102 q->current_id = next_id;
103 q->current_ts = next_ts;
104 } else {
105 if (sequence_number_older(next_id, q->current_id, next_ts, q->current_ts)) {
106 printf("nextid: %d current: %d\n", next_id, q->current_id);
107 q->current_id = (q->current_id + 1) % MAX_SEQU_NUM;
108 *success = 2; /* tell the decoder the packet is lost */
109 return NULL;
110 } else {
111 /* packet too old */
112 printf("packet too old\n");
113 *success = 0;
114 return NULL;
115 }
116 }
117 }
118
119 q->size--;
120 q->front++;
121
122 if (q->front == q->capacity)
123 q->front = 0;
124
125 *success = 1;
126 q->current_id = q->queue[front]->header->sequnum;
127 q->current_ts = q->queue[front]->header->timestamp;
128 return q->queue[front];
129}
130
131int empty_queue(struct jitter_buffer *q)
132{
133 while (q->size > 0) {
134 q->size--;
135 rtp_free_msg(NULL, q->queue[q->front]);
136 q->front++;
137
138 if (q->front == q->capacity)
139 q->front = 0;
140 }
141
142 q->id_set = 0;
143 q->queue_ready = 0;
144 return 0;
145}
146
147int queue(struct jitter_buffer *q, RTPMessage *pk)
148{
149 if (q->size == q->capacity) {
150 printf("buffer full, emptying buffer...\n");
151 empty_queue(q);
152 return 0;
153 }
154
155 if (q->size > 8)
156 q->queue_ready = 1;
157
158 ++q->size;
159 ++q->rear;
160
161 if (q->rear == q->capacity)
162 q->rear = 0;
163
164 q->queue[q->rear] = pk;
165
166 int a;
167 int b;
168 int j;
169 a = q->rear;
170
171 for (j = 0; j < q->size - 1; ++j) {
172 b = a - 1;
173
174 if (b < 0)
175 b += q->capacity;
176
177 if (sequence_number_older(q->queue[b]->header->sequnum, q->queue[a]->header->sequnum,
178 q->queue[b]->header->timestamp, q->queue[a]->header->timestamp)) {
179 RTPMessage *temp;
180 temp = q->queue[a];
181 q->queue[a] = q->queue[b];
182 q->queue[b] = temp;
183 printf("had to swap\n");
184 } else {
185 break;
186 }
187
188 a -= 1;
189
190 if (a < 0)
191 a += q->capacity;
192 }
193
194 if (pk)
195 return 1;
196
197 return 0;
198}
199
200
201int init_video_decoder(CodecState *cs)
202{
203 if (vpx_codec_dec_init_ver(&cs->v_decoder, VIDEO_CODEC_DECODER_INTERFACE, NULL, 0, VPX_DECODER_ABI_VERSION) != VPX_CODEC_OK) {
204 fprintf(stderr, "Init video_decoder failed!\n");
205 return -1;
206 }
207
208 return 0;
209}
210
211int init_audio_decoder(CodecState *cs, uint32_t audio_channels)
212{
213 int rc;
214 cs->audio_decoder = opus_decoder_create(cs->audio_sample_rate, audio_channels, &rc );
215
216 if ( rc != OPUS_OK ){
217 fprintf(stderr, "Error while starting audio decoder!\n");
218 return -1;
219 }
220
221 return 0;
222}
223
224
225int init_video_encoder(CodecState *cs, uint16_t width, uint16_t height, uint32_t video_bitrate)
226{
227 vpx_codec_enc_cfg_t cfg;
228 int res = vpx_codec_enc_config_default(VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0);
229 if(res) {
230 printf("Failed to get config: %s\n", vpx_codec_err_to_string(res));
231 return -1;
232 }
233
234 cfg.rc_target_bitrate = video_bitrate;
235 cfg.g_w = width;
236 cfg.g_h = height;
237 if(vpx_codec_enc_init_ver(&cs->v_encoder, VIDEO_CODEC_ENCODER_INTERFACE, &cfg, 0, VPX_ENCODER_ABI_VERSION) != VPX_CODEC_OK) {
238 fprintf(stderr, "Failed to initialize encoder\n");
239 return -1;
240 }
241 return 0;
242}
243
244int init_audio_encoder(CodecState *cs, uint32_t audio_channels)
245{
246 int err = OPUS_OK;
247 cs->audio_encoder = opus_encoder_create(cs->audio_sample_rate, audio_channels, OPUS_APPLICATION_AUDIO, &err);
248 err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate));
249 err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10));
250
251
252 return err == OPUS_OK ? 0 : -1;
253}
254
255
256CodecState* codec_init_session ( uint32_t audio_bitrate,
257 uint16_t audio_frame_duration,
258 uint32_t audio_sample_rate,
259 uint32_t audio_channels,
260 uint16_t video_width,
261 uint16_t video_height,
262 uint32_t video_bitrate )
263{
264 CodecState* _retu = calloc(sizeof(CodecState), 1);
265 assert(_retu);
266
267 _retu->audio_bitrate = audio_bitrate;
268 _retu->audio_sample_rate = audio_sample_rate;
269
270 /* Encoders */
271 if (!video_width || !video_height) {
272 video_width = 320;
273 video_height = 240;
274 }
275
276 if ( 0 == init_video_encoder(_retu, video_width, video_height, video_bitrate) )
277 printf("Video encoder initialized!\n");
278
279 if ( 0 == init_audio_encoder(_retu, audio_channels) )
280 printf("Audio encoder initialized!\n");
281
282
283 /* Decoders */
284 if ( 0 == init_video_decoder(_retu) )
285 printf("Video decoder initialized!\n");
286
287 if ( 0 == init_audio_decoder(_retu, audio_channels) )
288 printf("Audio decoder initialized!\n");
289
290
291 return _retu;
292}
293
294void codec_terminate_session ( CodecState* cs )
295{
296 if ( cs->audio_encoder ) {
297 opus_encoder_destroy(cs->audio_encoder);
298 printf("Terminated encoder!\n");
299 }
300
301 if ( cs->audio_decoder ) {
302 opus_decoder_destroy(cs->audio_decoder);
303 printf("Terminated decoder!\n");
304 }
305
306 /* TODO: Terminate video */
307 vpx_codec_destroy(&cs->v_decoder);
308 vpx_codec_destroy(&cs->v_encoder);
309}
diff --git a/toxav/media.h b/toxav/media.h
new file mode 100644
index 00000000..030a36ac
--- /dev/null
+++ b/toxav/media.h
@@ -0,0 +1,80 @@
1/** media.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 _AVCODEC_H_
25#define _AVCODEC_H_
26
27#include <stdio.h>
28#include <math.h>
29#include <pthread.h>
30
31#include <vpx/vpx_decoder.h>
32#include <vpx/vpx_encoder.h>
33#include <vpx/vp8dx.h>
34#include <vpx/vp8cx.h>
35#define VIDEO_CODEC_DECODER_INTERFACE (vpx_codec_vp8_dx())
36#define VIDEO_CODEC_ENCODER_INTERFACE (vpx_codec_vp8_cx())
37
38/* Audio encoding/decoding */
39#include <opus/opus.h>
40
41
42typedef struct _CodecState{
43
44 /* video encoding */
45 vpx_codec_ctx_t v_encoder;
46 uint32_t frame_counter;
47
48 /* video decoding */
49 vpx_codec_ctx_t v_decoder;
50
51 /* audio encoding */
52 OpusEncoder *audio_encoder;
53 int audio_bitrate;
54 int audio_sample_rate;
55
56 /* audio decoding */
57 OpusDecoder *audio_decoder;
58
59} CodecState;
60
61typedef struct _RTPMessage RTPMessage;
62
63struct jitter_buffer *create_queue(int capacity);
64int empty_queue(struct jitter_buffer *q);
65
66int queue(struct jitter_buffer *q, RTPMessage *pk);
67RTPMessage *dequeue(struct jitter_buffer *q, int *success);
68
69
70CodecState* codec_init_session ( uint32_t audio_bitrate,
71 uint16_t audio_frame_duration,
72 uint32_t audio_sample_rate,
73 uint32_t audio_channels,
74 uint16_t video_width,
75 uint16_t video_height,
76 uint32_t video_bitrate );
77
78void codec_terminate_session(CodecState* cs);
79
80#endif
diff --git a/toxav/msi.c b/toxav/msi.c
new file mode 100644
index 00000000..84ab973f
--- /dev/null
+++ b/toxav/msi.c
@@ -0,0 +1,1379 @@
1/** toxmsi.c
2 *
3 * Copyright (C) 2013 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 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif /* HAVE_CONFIG_H */
28
29#define _BSD_SOURCE
30
31#include "msi.h"
32#include "event.h"
33#include "../toxcore/util.h"
34#include "../toxcore/network.h"
35#include "../toxcore/Messenger.h"
36
37#include <assert.h>
38#include <unistd.h>
39#include <string.h>
40#include <stdlib.h>
41
42#define same(x, y) strcmp((const char*) x, (const char*) y) == 0
43
44#define MSI_MAXMSG_SIZE 1024
45
46#define TYPE_REQUEST 1
47#define TYPE_RESPONSE 2
48
49unsigned char* VERSION_STRING = (unsigned char*)"0.3.1";
50#define VERSION_STRLEN 5
51
52#define CT_AUDIO_HEADER_VALUE "AUDIO"
53#define CT_VIDEO_HEADER_VALUE "VIDEO"
54
55
56/* Define default timeout for a request.
57 * There is no behavior specified by the msi on what will
58 * client do on timeout, but to call timeout callback.
59 */
60#define m_deftout 10000 /* in milliseconds */
61
62/**
63 * Protocol:
64 *
65 * | desc. ( 1 byte ) | length ( 2 bytes ) | value ( length bytes ) |
66 *
67 * ie.
68 *
69 * | 0x1 | 0x0 0x7 | "version"
70 *
71 * Means: it's field value with length of 7 bytes and value of "version"
72 * It's similar to amp protocol
73 */
74
75
76#define GENERIC_HEADER(header) \
77typedef struct _MSIHeader##header { \
78uint8_t* header_value; \
79uint16_t size; \
80} MSIHeader##header;
81
82
83GENERIC_HEADER ( Version )
84GENERIC_HEADER ( Request )
85GENERIC_HEADER ( Response )
86GENERIC_HEADER ( CallType )
87GENERIC_HEADER ( UserAgent )
88GENERIC_HEADER ( CallId )
89GENERIC_HEADER ( Info )
90GENERIC_HEADER ( Reason )
91GENERIC_HEADER ( CryptoKey )
92GENERIC_HEADER ( Nonce )
93
94
95/**
96 * @brief This is the message structure. It contains all of the headers and
97 * destination/source of the message stored in friend_id.
98 *
99 */
100typedef struct _MSIMessage {
101
102 MSIHeaderVersion version;
103 MSIHeaderRequest request;
104 MSIHeaderResponse response;
105 MSIHeaderCallType calltype;
106 MSIHeaderUserAgent useragent;
107 MSIHeaderInfo info;
108 MSIHeaderReason reason;
109 MSIHeaderCallId callid;
110 MSIHeaderCryptoKey cryptokey;
111 MSIHeaderNonce nonce;
112
113 struct _MSIMessage* next;
114
115 int friend_id;
116
117} MSIMessage;
118
119
120
121static MSICallback callbacks[10] = {0};
122
123
124/* define strings for the identifiers */
125#define VERSION_FIELD "Version"
126#define REQUEST_FIELD "Request"
127#define RESPONSE_FIELD "Response"
128#define INFO_FIELD "INFO"
129#define REASON_FIELD "Reason"
130#define CALLTYPE_FIELD "Call-type"
131#define USERAGENT_FIELD "User-agent"
132#define CALLID_FIELD "Call-id"
133#define CRYPTOKEY_FIELD "Crypto-key"
134#define NONCE_FIELD "Nonce"
135
136/* protocol descriptors */
137#define end_byte 0x0
138#define field_byte 0x1
139#define value_byte 0x2
140
141
142typedef enum {
143 invite,
144 start,
145 cancel,
146 reject,
147 end,
148
149} MSIRequest;
150
151
152/**
153 * @brief Get string value for request.
154 *
155 * @param request The request.
156 * @return const uint8_t* The string
157 */
158static inline const uint8_t *stringify_request ( MSIRequest request ) {
159 static const uint8_t* strings[] = {
160 ( uint8_t* ) "INVITE",
161 ( uint8_t* ) "START",
162 ( uint8_t* ) "CANCEL",
163 ( uint8_t* ) "REJECT",
164 ( uint8_t* ) "END"
165 };
166
167 return strings[request];
168}
169
170
171typedef enum {
172 ringing,
173 starting,
174 ending,
175 error
176
177} MSIResponse;
178
179
180/**
181 * @brief Get string value for response.
182 *
183 * @param response The response.
184 * @return const uint8_t* The string
185 */
186static inline const uint8_t *stringify_response ( MSIResponse response ) {
187 static const uint8_t* strings[] = {
188 ( uint8_t* ) "ringing",
189 ( uint8_t* ) "starting",
190 ( uint8_t* ) "ending",
191 ( uint8_t* ) "error"
192 };
193
194 return strings[response];
195}
196
197
198#define ON_HEADER(iterator, header, descriptor, size_const) \
199( memcmp(iterator, descriptor, size_const) == 0){ /* Okay */ \
200 iterator += size_const; /* Set iterator at begining of value part */ \
201 if ( *iterator != value_byte ) { assert(0); return -1; }\
202 iterator ++;\
203 uint16_t _value_size = (uint16_t) *(iterator ) << 8 | \
204 (uint16_t) *(iterator + 1); \
205 header.header_value = calloc(sizeof(uint8_t), _value_size); \
206 header.size = _value_size; \
207 memcpy(header.header_value, iterator + 2, _value_size);\
208 iterator = iterator + 2 + _value_size; /* set iterator at new header or end_byte */ \
209}
210
211/**
212 * @brief Parse raw 'data' received from socket into MSIMessage struct.
213 * Every message has to have end value of 'end_byte' or _undefined_ behavior
214 * occures. The best practice is to check the end of the message at the handle_packet.
215 *
216 * @param msg Container.
217 * @param data The data.
218 * @return int
219 * @retval -1 Error occured.
220 * @retval 0 Success.
221 */
222int parse_raw_data ( MSIMessage* msg, const uint8_t* data, uint16_t length ) {
223 assert ( msg );
224
225 if ( data[length - 1] ) /* End byte must have value 0 */
226 return -1;
227
228 const uint8_t* _it = data;
229
230 while ( *_it ) {/* until end_byte is hit */
231
232 uint16_t itedlen = (_it - data) + 2;
233
234 if ( *_it == field_byte && itedlen < length ) {
235
236 uint16_t _size = ( uint16_t ) * ( _it + 1 ) << 8 |
237 ( uint16_t ) * ( _it + 2 );
238
239 if ( itedlen + _size > length ) return -1;
240
241 _it += 3; /* place it at the field value beginning */
242
243 switch ( _size ) { /* Compare the size of the hardcoded values ( vary fast and convenient ) */
244
245 case 4: { /* INFO header */
246 if ON_HEADER ( _it, msg->info, INFO_FIELD, 4 )
247 }
248 break;
249
250 case 5: { /* NONCE header */
251 if ON_HEADER ( _it, msg->nonce, NONCE_FIELD, 5 )
252 }
253 break;
254
255 case 6: { /* Reason header */
256 if ON_HEADER ( _it, msg->reason, REASON_FIELD, 6 )
257 }
258 break;
259
260 case 7: { /* Version, Request, Call-id headers */
261 if ON_HEADER ( _it, msg->version, VERSION_FIELD, 7 )
262 else if ON_HEADER ( _it, msg->request, REQUEST_FIELD, 7 )
263 else if ON_HEADER ( _it, msg->callid, CALLID_FIELD, 7 )
264 }
265 break;
266
267 case 8: { /* Response header */
268 if ON_HEADER ( _it, msg->response, RESPONSE_FIELD, 8 )
269 }
270 break;
271
272 case 9: { /* Call-type header */
273 if ON_HEADER ( _it, msg->calltype, CALLTYPE_FIELD, 9 )
274 }
275 break;
276
277 case 10: { /* User-agent, Crypto-key headers */
278 if ON_HEADER ( _it, msg->useragent, USERAGENT_FIELD, 10 )
279 else if ON_HEADER ( _it, msg->cryptokey, CRYPTOKEY_FIELD, 10 )
280 }
281 break;
282
283 default:
284 return -1;
285 }
286 } else return -1;
287 /* If it's anything else return failure as the message is invalid */
288
289 }
290
291 return 0;
292}
293
294
295#define ALLOCATE_HEADER( var, mheader_value, t_size) \
296var.header_value = calloc(sizeof *mheader_value, t_size); \
297memcpy(var.header_value, mheader_value, t_size); \
298var.size = t_size;
299
300
301/**
302 * @brief Speaks for it self.
303 *
304 * @param msg The message.
305 * @return void
306 */
307void free_message ( MSIMessage* msg ) {
308 assert ( msg );
309
310 free ( msg->calltype.header_value );
311 free ( msg->request.header_value );
312 free ( msg->response.header_value );
313 free ( msg->useragent.header_value );
314 free ( msg->version.header_value );
315 free ( msg->info.header_value );
316 free ( msg->cryptokey.header_value );
317 free ( msg->nonce.header_value );
318 free ( msg->reason.header_value );
319 free ( msg->callid.header_value );
320
321 free ( msg );
322}
323
324
325/**
326 * @brief Create the message.
327 *
328 * @param type Request or response.
329 * @param type_id Type of request/response.
330 * @return MSIMessage* Created message.
331 * @retval NULL Error occured.
332 */
333MSIMessage* msi_new_message ( uint8_t type, const uint8_t* type_id ) {
334 MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 );
335 assert ( _retu );
336
337 if ( type == TYPE_REQUEST ) {
338 ALLOCATE_HEADER ( _retu->request, type_id, strlen ( (const char*)type_id ) )
339
340 } else if ( type == TYPE_RESPONSE ) {
341 ALLOCATE_HEADER ( _retu->response, type_id, strlen ( (const char*)type_id ) )
342
343 } else {
344 free_message ( _retu );
345 return NULL;
346 }
347
348 ALLOCATE_HEADER ( _retu->version, VERSION_STRING, strlen ( (const char*)VERSION_STRING ) )
349
350 return _retu;
351}
352
353
354/**
355 * @brief Parse data from handle_packet.
356 *
357 * @param data The data.
358 * @return MSIMessage* Parsed message.
359 * @retval NULL Error occured.
360 */
361MSIMessage* parse_message ( const uint8_t* data, uint16_t length ) {
362 assert ( data );
363
364 MSIMessage* _retu = calloc ( sizeof ( MSIMessage ), 1 );
365 assert ( _retu );
366
367 memset ( _retu, 0, sizeof ( MSIMessage ) );
368
369 if ( parse_raw_data ( _retu, data, length ) == -1 ) {
370
371 free_message ( _retu );
372 return NULL;
373 }
374
375 if ( !_retu->version.header_value || VERSION_STRLEN != _retu->version.size ||
376 memcmp ( _retu->version.header_value, VERSION_STRING, VERSION_STRLEN ) != 0 ) {
377
378 free_message ( _retu );
379 return NULL;
380 }
381
382 return _retu;
383}
384
385
386
387/**
388 * @brief Speaks for it self.
389 *
390 * @param dest Container.
391 * @param header_field Field.
392 * @param header_value Field value.
393 * @param value_len Length of field value.
394 * @param length Pointer to container length.
395 * @return uint8_t* Iterated container.
396 */
397uint8_t* append_header_to_string (
398 uint8_t* dest,
399 const uint8_t* header_field,
400 const uint8_t* header_value,
401 uint16_t value_len,
402 uint16_t* length )
403{
404 assert ( dest );
405 assert ( header_value );
406 assert ( header_field );
407
408 const uint8_t* _hvit = header_value;
409 uint16_t _total = 6 + value_len; /* 6 is known plus header value len + field len*/
410
411 *dest = field_byte; /* Set the first byte */
412
413 uint8_t* _getback_byte = dest + 1; /* remeber the byte we were on */
414 dest += 3; /* swith to 4th byte where field value starts */
415
416 /* Now set the field value and calculate it's length */
417 uint16_t _i = 0;
418 for ( ; header_field[_i]; ++_i ) {
419 *dest = header_field[_i];
420 ++dest;
421 };
422 _total += _i;
423
424 /* Now set the length of the field byte */
425 *_getback_byte = ( uint8_t ) _i >> 8;
426 _getback_byte++;
427 *_getback_byte = ( uint8_t ) _i;
428
429 /* for value part do it regulary */
430 *dest = value_byte;
431 dest++;
432
433 *dest = ( uint8_t ) value_len >> 8;
434 dest++;
435 *dest = ( uint8_t ) value_len;
436 dest++;
437
438 for ( _i = value_len; _i; --_i ) {
439 *dest = *_hvit;
440 ++_hvit;
441 ++dest;
442 }
443
444 *length += _total;
445 return dest;
446}
447
448
449#define CLEAN_ASSIGN(added, var, field, header)\
450if ( header.header_value ) { var = append_header_to_string(var, (const uint8_t*)field, header.header_value, header.size, &added); }
451
452
453/**
454 * @brief Convert MSIMessage struct to _sendable_ string.
455 *
456 * @param msg The message.
457 * @param dest Destination.
458 * @return uint16_t It's final size.
459 */
460uint16_t message_to_string ( MSIMessage* msg, uint8_t* dest ) {
461 assert ( msg );
462 assert ( dest );
463
464 uint8_t* _iterated = dest;
465 uint16_t _size = 0;
466
467 CLEAN_ASSIGN ( _size, _iterated, VERSION_FIELD, msg->version );
468 CLEAN_ASSIGN ( _size, _iterated, REQUEST_FIELD, msg->request );
469 CLEAN_ASSIGN ( _size, _iterated, RESPONSE_FIELD, msg->response );
470 CLEAN_ASSIGN ( _size, _iterated, CALLTYPE_FIELD, msg->calltype );
471 CLEAN_ASSIGN ( _size, _iterated, USERAGENT_FIELD, msg->useragent );
472 CLEAN_ASSIGN ( _size, _iterated, INFO_FIELD, msg->info );
473 CLEAN_ASSIGN ( _size, _iterated, CALLID_FIELD, msg->callid );
474 CLEAN_ASSIGN ( _size, _iterated, REASON_FIELD, msg->reason );
475 CLEAN_ASSIGN ( _size, _iterated, CRYPTOKEY_FIELD, msg->cryptokey );
476 CLEAN_ASSIGN ( _size, _iterated, NONCE_FIELD, msg->nonce );
477
478 *_iterated = end_byte;
479 _size ++;
480
481 return _size;
482}
483
484
485#define GENERIC_SETTER_DEFINITION(header) \
486void msi_msg_set_##header ( MSIMessage* _msg, const uint8_t* header_value, uint16_t _size ) \
487{ assert(_msg); assert(header_value); \
488 free(_msg->header.header_value); \
489 ALLOCATE_HEADER( _msg->header, header_value, _size )}
490
491GENERIC_SETTER_DEFINITION ( calltype )
492GENERIC_SETTER_DEFINITION ( useragent )
493GENERIC_SETTER_DEFINITION ( reason )
494GENERIC_SETTER_DEFINITION ( info )
495GENERIC_SETTER_DEFINITION ( callid )
496GENERIC_SETTER_DEFINITION ( cryptokey )
497GENERIC_SETTER_DEFINITION ( nonce )
498
499
500/**
501 * @brief Generate _random_ alphanumerical string.
502 *
503 * @param str Destination.
504 * @param size Size of string.
505 * @return void
506 */
507void t_randomstr ( uint8_t* str, size_t size ) {
508 assert ( str );
509
510 static const uint8_t _bytes[] =
511 "0123456789"
512 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
513 "abcdefghijklmnopqrstuvwxyz";
514
515 size_t _it = 0;
516
517 for ( ; _it < size; _it++ ) {
518 str[_it] = _bytes[ random_int() % 61 ];
519 }
520}
521
522
523typedef enum {
524 error_deadcall = 1, /* has call id but it's from old call */
525 error_id_mismatch, /* non-existing call */
526
527 error_no_callid, /* not having call id */
528 error_no_call, /* no call in session */
529 error_no_crypto_key, /* no crypto key */
530
531 error_busy
532
533} MSICallError; /* Error codes */
534
535
536/**
537 * @brief Stringify error code.
538 *
539 * @param error_code The code.
540 * @return const uint8_t* The string.
541 */
542static inline const uint8_t *stringify_error ( MSICallError error_code ) {
543 static const uint8_t* strings[] = {
544 ( uint8_t* ) "",
545 ( uint8_t* ) "Using dead call",
546 ( uint8_t* ) "Call id not set to any call",
547 ( uint8_t* ) "Call id not available",
548 ( uint8_t* ) "No active call in session",
549 ( uint8_t* ) "No Crypto-key set",
550 ( uint8_t* ) "Callee busy"
551 };
552
553 return strings[error_code];
554}
555
556
557/**
558 * @brief Convert error_code into string.
559 *
560 * @param error_code The code.
561 * @return const uint8_t* The string.
562 */
563static inline const uint8_t *stringify_error_code ( MSICallError error_code ) {
564 static const uint8_t* strings[] = {
565 ( uint8_t* ) "",
566 ( uint8_t* ) "1",
567 ( uint8_t* ) "2",
568 ( uint8_t* ) "3",
569 ( uint8_t* ) "4",
570 ( uint8_t* ) "5",
571 ( uint8_t* ) "6"
572 };
573
574 return strings[error_code];
575}
576
577
578/**
579 * @brief Speaks for it self.
580 *
581 * @param session Control session.
582 * @param msg The message.
583 * @param to Where to.
584 * @return int
585 * @retval -1 Error occured.
586 * @retval 0 Success.
587 */
588int send_message ( MSISession* session, MSIMessage* msg, uint32_t to )
589{
590 msi_msg_set_callid ( msg, session->call->id, CALL_ID_LEN );
591
592 uint8_t _msg_string_final [MSI_MAXMSG_SIZE];
593 uint16_t _length = message_to_string ( msg, _msg_string_final );
594
595 return m_msi_packet(session->messenger_handle, to, _msg_string_final, _length) ? 0 : -1;
596}
597
598
599/**
600 * @brief Speaks for it self.
601 *
602 * @param session Control session.
603 * @param msg The message.
604 * @param peer_id The peer.
605 * @return void
606 */
607void flush_peer_type ( MSISession* session, MSIMessage* msg, int peer_id ) {
608 if ( msg->calltype.header_value ) {
609 if ( strcmp ( ( const char* ) msg->calltype.header_value, CT_AUDIO_HEADER_VALUE ) == 0 ) {
610 session->call->type_peer[peer_id] = type_audio;
611
612 } else if ( strcmp ( ( const char* ) msg->calltype.header_value, CT_VIDEO_HEADER_VALUE ) == 0 ) {
613 session->call->type_peer[peer_id] = type_video;
614 } else {} /* Error */
615 } else {} /* Error */
616}
617
618void handle_remote_connection_change(Messenger* messenger, int friend_num, uint8_t status, void* session_p)
619{
620 MSISession* session = session_p;
621
622 switch ( status )
623 {
624 case 0: /* Went offline */
625 {
626 if ( session->call ) {
627 int i = 0;
628 for ( ; i < session->call->peer_count; i ++ )
629 if ( session->call->peers[i] == friend_num ) {
630 msi_stopcall(session); /* Stop the call for now */
631 return;
632 }
633 }
634 } break;
635
636 default: break;
637 }
638}
639
640/**
641 * @brief Sends error response to peer.
642 *
643 * @param session The session.
644 * @param errid The id.
645 * @param to Where to?
646 * @return int
647 * @retval 0 It's always success.
648 */
649int handle_error ( MSISession* session, MSICallError errid, uint32_t to ) {
650 MSIMessage* _msg_error = msi_new_message ( TYPE_RESPONSE, stringify_response ( error ) );
651
652 const uint8_t* _error_code_str = stringify_error_code ( errid );
653
654 msi_msg_set_reason ( _msg_error, _error_code_str, strlen ( ( const char* ) _error_code_str ) );
655 send_message ( session, _msg_error, to );
656 free_message ( _msg_error );
657
658 session->last_error_id = errid;
659 session->last_error_str = stringify_error ( errid );
660
661 event.rise ( callbacks[MSI_OnError], session->agent_handler );
662
663 return 0;
664}
665
666
667/**
668 * @brief Determine the error if any.
669 *
670 * @param session Control session.
671 * @param msg The message.
672 * @return int
673 * @retval -1 No error.
674 * @retval 0 Error occured and response sent.
675 */
676int has_call_error ( MSISession* session, MSIMessage* msg ) {
677 if ( !msg->callid.header_value ) {
678 return handle_error ( session, error_no_callid, msg->friend_id );
679
680 } else if ( !session->call ) {
681 return handle_error ( session, error_no_call, msg->friend_id );
682
683 } else if ( memcmp ( session->call->id, msg->callid.header_value, CALL_ID_LEN ) != 0 ) {
684 return handle_error ( session, error_id_mismatch, msg->friend_id );
685
686 }
687
688 return -1;
689}
690
691
692/**
693 * @brief Function called at request timeout.
694 *
695 * @param arg Control session
696 * @return void*
697 */
698void* handle_timeout ( void* arg )
699{
700 /* Send hangup either way */
701 MSISession* _session = arg;
702
703 if ( _session && _session->call ) {
704
705 uint32_t* _peers = _session->call->peers;
706 uint16_t _peer_count = _session->call->peer_count;
707
708
709 /* Cancel all? */
710 uint16_t _it = 0;
711 for ( ; _it < _peer_count; _it++ )
712 msi_cancel ( arg, _peers[_it], (const uint8_t*)"Timeout" );
713
714 }
715
716 ( *callbacks[MSI_OnRequestTimeout] ) ( _session->agent_handler );
717 ( *callbacks[MSI_OnEnding ] ) ( _session->agent_handler );
718
719 return NULL;
720}
721
722
723/**
724 * @brief Add peer to peer list.
725 *
726 * @param call What call.
727 * @param peer_id Its id.
728 * @return void
729 */
730void add_peer( MSICall* call, int peer_id )
731{
732 if ( !call->peers ) {
733 call->peers = calloc(sizeof(int), 1);
734 call->peer_count = 1;
735 } else{
736 call->peer_count ++;
737 call->peers = realloc( call->peers, sizeof(int) * call->peer_count);
738 }
739
740 call->peers[call->peer_count - 1] = peer_id;
741}
742
743
744/**
745 * @brief Speaks for it self.
746 *
747 * @param session Control session.
748 * @param peers Amount of peers. (Currently it only supports 1)
749 * @param ringing_timeout Ringing timeout.
750 * @return MSICall* The created call.
751 */
752MSICall* init_call ( MSISession* session, int peers, int ringing_timeout ) {
753 assert ( session );
754 assert ( peers );
755
756 MSICall* _call = calloc ( sizeof ( MSICall ), 1 );
757 _call->type_peer = calloc ( sizeof ( MSICallType ), peers );
758
759 assert ( _call );
760 assert ( _call->type_peer );
761
762 /*_call->_participant_count = _peers;*/
763
764 _call->request_timer_id = 0;
765 _call->ringing_timer_id = 0;
766
767 _call->key_local = NULL;
768 _call->key_peer = NULL;
769 _call->nonce_local = NULL;
770 _call->nonce_peer = NULL;
771
772 _call->ringing_tout_ms = ringing_timeout;
773
774 pthread_mutex_init ( &_call->mutex, NULL );
775
776 return _call;
777}
778
779
780/**
781 * @brief Terminate the call.
782 *
783 * @param session Control session.
784 * @return int
785 * @retval -1 Error occured.
786 * @retval 0 Success.
787 */
788int terminate_call ( MSISession* session ) {
789 assert ( session );
790
791 if ( !session->call )
792 return -1;
793
794
795 /* Check event loop and cancel timed events if there are any
796 * NOTE: This has to be done before possibly
797 * locking the mutex the second time
798 */
799 event.timer_release ( session->call->request_timer_id );
800 event.timer_release ( session->call->ringing_timer_id );
801
802 /* Get a handle */
803 pthread_mutex_lock ( &session->call->mutex );
804
805 MSICall* _call = session->call;
806 session->call = NULL;
807
808 free ( _call->type_peer );
809 free ( _call->key_local );
810 free ( _call->key_peer );
811 free ( _call->peers);
812
813 /* Release handle */
814 pthread_mutex_unlock ( &_call->mutex );
815
816 pthread_mutex_destroy ( &_call->mutex );
817
818 free ( _call );
819
820 return 0;
821}
822
823
824/********** Request handlers **********/
825int handle_recv_invite ( MSISession* session, MSIMessage* msg ) {
826 assert ( session );
827
828 if ( session->call ) {
829 handle_error ( session, error_busy, msg->friend_id );
830 return 0;
831 }
832 if ( !msg->callid.header_value ) {
833 handle_error ( session, error_no_callid, msg->friend_id );
834 return 0;
835 }
836
837 session->call = init_call ( session, 1, 0 );
838 memcpy ( session->call->id, msg->callid.header_value, CALL_ID_LEN );
839 session->call->state = call_starting;
840
841 add_peer( session->call, msg->friend_id);
842
843 flush_peer_type ( session, msg, 0 );
844
845 MSIMessage* _msg_ringing = msi_new_message ( TYPE_RESPONSE, stringify_response ( ringing ) );
846 send_message ( session, _msg_ringing, msg->friend_id );
847 free_message ( _msg_ringing );
848
849 event.rise ( callbacks[MSI_OnInvite], session->agent_handler );
850
851 return 1;
852}
853int handle_recv_start ( MSISession* session, MSIMessage* msg ) {
854 assert ( session );
855
856 if ( has_call_error ( session, msg ) == 0 )
857 return 0;
858
859 if ( !msg->cryptokey.header_value )
860 return handle_error ( session, error_no_crypto_key, msg->friend_id );
861
862 session->call->state = call_active;
863
864 session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
865 memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES );
866
867 session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
868 memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES );
869
870 flush_peer_type ( session, msg, 0 );
871
872 event.rise ( callbacks[MSI_OnStart], session->agent_handler );
873
874 return 1;
875}
876int handle_recv_reject ( MSISession* session, MSIMessage* msg ) {
877 assert ( session );
878
879 if ( has_call_error ( session, msg ) == 0 )
880 return 0;
881
882
883 MSIMessage* _msg_end = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) );
884 send_message ( session, _msg_end, msg->friend_id );
885 free_message ( _msg_end );
886
887 event.timer_release ( session->call->request_timer_id );
888 event.rise ( callbacks[MSI_OnReject], session->agent_handler );
889 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
890
891 return 1;
892}
893int handle_recv_cancel ( MSISession* session, MSIMessage* msg ) {
894 assert ( session );
895
896 if ( has_call_error ( session, msg ) == 0 )
897 return 0;
898
899
900 terminate_call ( session );
901
902 event.rise ( callbacks[MSI_OnCancel], session->agent_handler );
903
904 return 1;
905}
906int handle_recv_end ( MSISession* session, MSIMessage* msg ) {
907 assert ( session );
908
909 if ( has_call_error ( session, msg ) == 0 )
910 return 0;
911
912
913 MSIMessage* _msg_ending = msi_new_message ( TYPE_RESPONSE, stringify_response ( ending ) );
914 send_message ( session, _msg_ending, msg->friend_id );
915 free_message ( _msg_ending );
916
917 terminate_call ( session );
918
919 event.rise ( callbacks[MSI_OnEnd], session->agent_handler );
920
921 return 1;
922}
923
924/********** Response handlers **********/
925int handle_recv_ringing ( MSISession* session, MSIMessage* msg ) {
926 assert ( session );
927
928 if ( has_call_error ( session, msg ) == 0 )
929 return 0;
930
931 session->call->ringing_timer_id = event.timer_alloc ( handle_timeout, session, session->call->ringing_tout_ms );
932 event.rise ( callbacks[MSI_OnRinging], session->agent_handler );
933
934 return 1;
935}
936int handle_recv_starting ( MSISession* session, MSIMessage* msg ) {
937 assert ( session );
938
939 if ( has_call_error ( session, msg ) == 0 )
940 return 0;
941
942 if ( !msg->cryptokey.header_value ) {
943 return handle_error ( session, error_no_crypto_key, msg->friend_id );
944 }
945
946 /* Generate local key/nonce to send */
947 session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
948 new_symmetric_key ( session->call->key_local );
949
950 session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
951 new_nonce ( session->call->nonce_local );
952
953 /* Save peer key/nonce */
954 session->call->key_peer = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
955 memcpy ( session->call->key_peer, msg->cryptokey.header_value, crypto_secretbox_KEYBYTES );
956
957 session->call->nonce_peer = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
958 memcpy ( session->call->nonce_peer, msg->nonce.header_value, crypto_box_NONCEBYTES );
959
960 session->call->state = call_active;
961
962 MSIMessage* _msg_start = msi_new_message ( TYPE_REQUEST, stringify_request ( start ) );
963 msi_msg_set_cryptokey ( _msg_start, session->call->key_local, crypto_secretbox_KEYBYTES );
964 msi_msg_set_nonce ( _msg_start, session->call->nonce_local, crypto_box_NONCEBYTES );
965 send_message ( session, _msg_start, msg->friend_id );
966 free_message ( _msg_start );
967
968 flush_peer_type ( session, msg, 0 );
969
970 event.rise ( callbacks[MSI_OnStarting], session->agent_handler );
971 event.timer_release ( session->call->ringing_timer_id );
972
973 return 1;
974}
975int handle_recv_ending ( MSISession* session, MSIMessage* msg ) {
976 assert ( session );
977
978 if ( has_call_error ( session, msg ) == 0 )
979 return 0;
980
981
982 terminate_call ( session );
983
984 event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
985
986 return 1;
987}
988int handle_recv_error ( MSISession* session, MSIMessage* msg ) {
989 assert ( session );
990 assert ( session->call );
991
992 /* Handle error accordingly */
993 if ( msg->reason.header_value ) {
994 session->last_error_id = atoi ( ( const char* ) msg->reason.header_value );
995 session->last_error_str = stringify_error ( session->last_error_id );
996 }
997
998 terminate_call ( session );
999
1000 event.rise ( callbacks[MSI_OnEnding], session->agent_handler );
1001
1002 return 1;
1003}
1004
1005
1006/**
1007 * @brief BASIC call flow:
1008 *
1009 * ALICE BOB
1010 * | invite --> |
1011 * | |
1012 * | <-- ringing |
1013 * | |
1014 * | <-- starting |
1015 * | |
1016 * | start --> |
1017 * | |
1018 * | <-- MEDIA TRANS --> |
1019 * | |
1020 * | end --> |
1021 * | |
1022 * | <-- ending |
1023 *
1024 * Alice calls Bob by sending invite packet.
1025 * Bob recvs the packet and sends an ringing packet;
1026 * which notifies Alice that her invite is acknowledged.
1027 * Ringing screen shown on both sides.
1028 * Bob accepts the invite for a call by sending starting packet.
1029 * Alice recvs the starting packet and sends the started packet to
1030 * inform Bob that she recved the starting packet.
1031 * Now the media transmission is established ( i.e. RTP transmission ).
1032 * Alice hangs up and sends end packet.
1033 * Bob recves the end packet and sends ending packet
1034 * as the acknowledgement that the call is ending.
1035 *
1036 *
1037 */
1038void msi_handle_packet ( Messenger* messenger, int source, uint8_t* data, uint16_t length, void* object )
1039{
1040 /* Unused */
1041 (void)messenger;
1042
1043 MSISession* _session = object;
1044 MSIMessage* _msg;
1045
1046 if ( !length ) return;
1047
1048 _msg = parse_message ( data, length );
1049
1050 if ( !_msg ) return;
1051
1052 _msg->friend_id = source;
1053
1054
1055 /* Now handle message */
1056
1057 if ( _msg->request.header_value ) { /* Handle request */
1058
1059 const uint8_t* _request_value = _msg->request.header_value;
1060
1061 if ( same ( _request_value, stringify_request ( invite ) ) ) {
1062 handle_recv_invite ( _session, _msg );
1063
1064 } else if ( same ( _request_value, stringify_request ( start ) ) ) {
1065 handle_recv_start ( _session, _msg );
1066
1067 } else if ( same ( _request_value, stringify_request ( cancel ) ) ) {
1068 handle_recv_cancel ( _session, _msg );
1069
1070 } else if ( same ( _request_value, stringify_request ( reject ) ) ) {
1071 handle_recv_reject ( _session, _msg );
1072
1073 } else if ( same ( _request_value, stringify_request ( end ) ) ) {
1074 handle_recv_end ( _session, _msg );
1075 }
1076
1077 else {
1078 free_message ( _msg );
1079 return;
1080 }
1081
1082 } else if ( _msg->response.header_value ) { /* Handle response */
1083
1084 const uint8_t* _response_value = _msg->response.header_value;
1085
1086 if ( same ( _response_value, stringify_response ( ringing ) ) ) {
1087 handle_recv_ringing ( _session, _msg );
1088
1089 } else if ( same ( _response_value, stringify_response ( starting ) ) ) {
1090 handle_recv_starting ( _session, _msg );
1091
1092 } else if ( same ( _response_value, stringify_response ( ending ) ) ) {
1093 handle_recv_ending ( _session, _msg );
1094
1095 } else if ( same ( _response_value, stringify_response ( error ) ) ) {
1096 handle_recv_error ( _session, _msg );
1097 } else {
1098 free_message ( _msg );
1099 return;
1100 }
1101
1102 /* Got response so cancel timer */
1103 if ( _session->call )
1104 event.timer_release ( _session->call->request_timer_id );
1105
1106 }
1107
1108 free_message ( _msg );
1109}
1110
1111
1112/********************************************************************************************************************
1113 * *******************************************************************************************************************
1114 ********************************************************************************************************************
1115 ********************************************************************************************************************
1116 ********************************************************************************************************************
1117 *
1118 *
1119 *
1120 * PUBLIC API FUNCTIONS IMPLEMENTATIONS
1121 *
1122 *
1123 *
1124 ********************************************************************************************************************
1125 ********************************************************************************************************************
1126 ********************************************************************************************************************
1127 ********************************************************************************************************************
1128 ********************************************************************************************************************/
1129
1130
1131
1132
1133
1134
1135
1136
1137/**
1138 * @brief Callback setter.
1139 *
1140 * @param callback The callback.
1141 * @param id The id.
1142 * @return void
1143 */
1144void msi_register_callback ( MSICallback callback, MSICallbackID id )
1145{
1146 callbacks[id] = callback;
1147}
1148
1149
1150/**
1151 * @brief Start the control session.
1152 *
1153 * @param messenger Tox* object.
1154 * @param user_agent User agent, i.e. 'Venom'; 'QT-gui'
1155 * @return MSISession* The created session.
1156 * @retval NULL Error occured.
1157 */
1158MSISession* msi_init_session ( Messenger* messenger, const uint8_t* ua_name ) {
1159 assert ( messenger );
1160
1161 MSISession* _retu = calloc ( sizeof ( MSISession ), 1 );
1162 assert ( _retu );
1163
1164 _retu->ua_name = ua_name;
1165 _retu->messenger_handle = messenger;
1166 _retu->agent_handler = NULL;
1167
1168 _retu->call = NULL;
1169
1170 _retu->frequ = 10000; /* default value? */
1171 _retu->call_timeout = 30000; /* default value? */
1172
1173
1174 m_callback_msi_packet(messenger, msi_handle_packet, _retu );
1175
1176 /* This is called when remote terminates session */
1177 m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, _retu);
1178
1179 return _retu;
1180}
1181
1182
1183/**
1184 * @brief Terminate control session.
1185 *
1186 * @param session The session
1187 * @return int
1188 */
1189int msi_terminate_session ( MSISession* session ) {
1190 assert ( session );
1191
1192 int _status = 0;
1193
1194 terminate_call ( session );
1195 m_callback_msi_packet((struct Messenger*) session->messenger_handle, NULL, NULL);
1196
1197
1198 /* TODO: Clean it up more? */
1199
1200 free ( session );
1201 return _status;
1202}
1203
1204
1205/**
1206 * @brief Send invite request to friend_id.
1207 *
1208 * @param session Control session.
1209 * @param call_type Type of the call. Audio or Video(both audio and video)
1210 * @param rngsec Ringing timeout.
1211 * @param friend_id The friend.
1212 * @return int
1213 */
1214int msi_invite ( MSISession* session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id ) {
1215 assert ( session );
1216
1217 MSIMessage* _msg_invite = msi_new_message ( TYPE_REQUEST, stringify_request ( invite ) );
1218
1219 session->call = init_call ( session, 1, rngsec ); /* Just one for now */
1220 t_randomstr ( session->call->id, CALL_ID_LEN );
1221
1222 add_peer(session->call, friend_id );
1223
1224 session->call->type_local = call_type;
1225 /* Do whatever with message */
1226
1227 if ( call_type == type_audio ) {
1228 msi_msg_set_calltype
1229 ( _msg_invite, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) );
1230 } else {
1231 msi_msg_set_calltype
1232 ( _msg_invite, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) );
1233 }
1234
1235 send_message ( session, _msg_invite, friend_id );
1236 free_message ( _msg_invite );
1237
1238 session->call->state = call_inviting;
1239
1240 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1241
1242 return 0;
1243}
1244
1245
1246/**
1247 * @brief Hangup active call.
1248 *
1249 * @param session Control session.
1250 * @return int
1251 * @retval -1 Error occured.
1252 * @retval 0 Success.
1253 */
1254int msi_hangup ( MSISession* session ) {
1255 assert ( session );
1256
1257 if ( !session->call || session->call->state != call_active )
1258 return -1;
1259
1260 MSIMessage* _msg_ending = msi_new_message ( TYPE_REQUEST, stringify_request ( end ) );
1261
1262 /* hangup for each peer */
1263 int _it = 0;
1264 for ( ; _it < session->call->peer_count; _it ++ )
1265 send_message ( session, _msg_ending, session->call->peers[_it] );
1266
1267
1268 free_message ( _msg_ending );
1269
1270 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1271
1272 return 0;
1273}
1274
1275
1276/**
1277 * @brief Answer active call request.
1278 *
1279 * @param session Control session.
1280 * @param call_type Answer with Audio or Video(both).
1281 * @return int
1282 */
1283int msi_answer ( MSISession* session, MSICallType call_type ) {
1284 assert ( session );
1285
1286 MSIMessage* _msg_starting = msi_new_message ( TYPE_RESPONSE, stringify_response ( starting ) );
1287 session->call->type_local = call_type;
1288
1289 if ( call_type == type_audio ) {
1290 msi_msg_set_calltype
1291 ( _msg_starting, ( const uint8_t* ) CT_AUDIO_HEADER_VALUE, strlen ( CT_AUDIO_HEADER_VALUE ) );
1292 } else {
1293 msi_msg_set_calltype
1294 ( _msg_starting, ( const uint8_t* ) CT_VIDEO_HEADER_VALUE, strlen ( CT_VIDEO_HEADER_VALUE ) );
1295 }
1296
1297 /* Now set the local encryption key and pass it with STARTING message */
1298
1299 session->call->key_local = calloc ( sizeof ( uint8_t ), crypto_secretbox_KEYBYTES );
1300 new_symmetric_key ( session->call->key_local );
1301
1302 session->call->nonce_local = calloc ( sizeof ( uint8_t ), crypto_box_NONCEBYTES );
1303 new_nonce ( session->call->nonce_local );
1304
1305 msi_msg_set_cryptokey ( _msg_starting, session->call->key_local, crypto_secretbox_KEYBYTES );
1306 msi_msg_set_nonce ( _msg_starting, session->call->nonce_local, crypto_box_NONCEBYTES );
1307
1308 send_message ( session, _msg_starting, session->call->peers[session->call->peer_count - 1] );
1309 free_message ( _msg_starting );
1310
1311 session->call->state = call_active;
1312
1313 return 0;
1314}
1315
1316
1317/**
1318 * @brief Cancel request.
1319 *
1320 * @param session Control session.
1321 * @param reason Set optional reason header. Pass NULL if none.
1322 * @return int
1323 */
1324int msi_cancel ( MSISession* session, uint32_t peer, const uint8_t* reason ) {
1325 assert ( session );
1326
1327 MSIMessage* _msg_cancel = msi_new_message ( TYPE_REQUEST, stringify_request ( cancel ) );
1328
1329 if ( reason ) msi_msg_set_reason(_msg_cancel, reason, strlen((const char*)reason));
1330
1331 send_message ( session, _msg_cancel, peer );
1332 free_message ( _msg_cancel );
1333
1334 terminate_call ( session );
1335
1336 return 0;
1337}
1338
1339
1340/**
1341 * @brief Reject request.
1342 *
1343 * @param session Control session.
1344 * @return int
1345 */
1346int msi_reject ( MSISession* session, const uint8_t* reason ) {
1347 assert ( session );
1348
1349 MSIMessage* _msg_reject = msi_new_message ( TYPE_REQUEST, stringify_request ( reject ) );
1350
1351 if ( reason ) msi_msg_set_reason(_msg_reject, reason, strlen((const char*)reason) + 1);
1352
1353 send_message ( session, _msg_reject, session->call->peers[session->call->peer_count - 1] );
1354 free_message ( _msg_reject );
1355
1356 session->call->request_timer_id = event.timer_alloc ( handle_timeout, session, m_deftout );
1357
1358 return 0;
1359}
1360
1361
1362/**
1363 * @brief Terminate the current call.
1364 *
1365 * @param session Control session.
1366 * @return int
1367 */
1368int msi_stopcall ( MSISession* session ) {
1369 assert ( session );
1370
1371 if ( !session->call )
1372 return -1;
1373
1374 /* just terminate it */
1375
1376 terminate_call ( session );
1377
1378 return 0;
1379} \ No newline at end of file
diff --git a/toxav/msi.h b/toxav/msi.h
new file mode 100644
index 00000000..5b693da4
--- /dev/null
+++ b/toxav/msi.h
@@ -0,0 +1,233 @@
1/** toxmsi.h
2 *
3 * Copyright (C) 2013 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 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24#ifndef __TOXMSI
25#define __TOXMSI
26
27#include <inttypes.h>
28#include <pthread.h>
29
30#include "../toxcore/Messenger.h"
31
32/* define size for call_id */
33#define CALL_ID_LEN 12
34
35
36typedef void* ( *MSICallback ) ( void* arg );
37
38
39/**
40 * @brief Call type identifier. Also used as rtp callback prefix.
41 */
42typedef enum {
43 type_audio = 70,
44 type_video
45} MSICallType;
46
47
48/**
49 * @brief Call state identifiers.
50 */
51typedef enum {
52 call_inviting, /* when sending call invite */
53 call_starting, /* when getting call invite */
54 call_active,
55 call_hold
56
57} MSICallState;
58
59
60
61/**
62 * @brief The call struct.
63 *
64 */
65typedef struct _MSICall { /* Call info structure */
66 MSICallState state;
67
68 MSICallType type_local; /* Type of payload user is ending */
69 MSICallType* type_peer; /* Type of payload others are sending */
70
71 uint8_t id[CALL_ID_LEN]; /* Random value identifying the call */
72
73 uint8_t* key_local; /* The key for encryption */
74 uint8_t* key_peer; /* The key for decryption */
75
76 uint8_t* nonce_local; /* Local nonce */
77 uint8_t* nonce_peer; /* Peer nonce */
78
79 int ringing_tout_ms; /* Ringing timeout in ms */
80
81 int request_timer_id; /* Timer id for outgoing request/action */
82 int ringing_timer_id; /* Timer id for ringing timeout */
83
84 pthread_mutex_t mutex; /* It's to be assumed that call will have
85 * seperate thread so add mutex
86 */
87 uint32_t* peers;
88 uint16_t peer_count;
89
90
91} MSICall;
92
93
94/**
95 * @brief Control session struct
96 *
97 */
98typedef struct _MSISession {
99
100 /* Call handler */
101 struct _MSICall* call;
102
103 int last_error_id; /* Determine the last error */
104 const uint8_t* last_error_str;
105
106 const uint8_t* ua_name;
107
108 void* agent_handler; /* Pointer to an object that is handling msi */
109 Messenger* messenger_handle;
110
111 uint32_t frequ;
112 uint32_t call_timeout; /* Time of the timeout for some action to end; 0 if infinite */
113
114
115} MSISession;
116
117
118/**
119 * @brief Callbacks ids that handle the states
120 */
121typedef enum {
122 /* Requests */
123 MSI_OnInvite,
124 MSI_OnStart,
125 MSI_OnCancel,
126 MSI_OnReject,
127 MSI_OnEnd,
128
129 /* Responses */
130 MSI_OnRinging,
131 MSI_OnStarting,
132 MSI_OnEnding,
133
134 /* Protocol */
135 MSI_OnError,
136 MSI_OnRequestTimeout
137
138} MSICallbackID;
139
140
141/**
142 * @brief Callback setter.
143 *
144 * @param callback The callback.
145 * @param id The id.
146 * @return void
147 */
148void msi_register_callback(MSICallback callback, MSICallbackID id);
149
150
151/**
152 * @brief Start the control session.
153 *
154 * @param messenger Tox* object.
155 * @param user_agent User agent, i.e. 'Venom'; 'QT-gui'
156 * @return MSISession* The created session.
157 * @retval NULL Error occured.
158 */
159MSISession* msi_init_session ( Messenger* messenger, const uint8_t* ua_name );
160
161
162/**
163 * @brief Terminate control session.
164 *
165 * @param session The session
166 * @return int
167 */
168int msi_terminate_session ( MSISession* session );
169
170
171/**
172 * @brief Send invite request to friend_id.
173 *
174 * @param session Control session.
175 * @param call_type Type of the call. Audio or Video(both audio and video)
176 * @param rngsec Ringing timeout.
177 * @param friend_id The friend.
178 * @return int
179 */
180int msi_invite ( MSISession* session, MSICallType call_type, uint32_t rngsec, uint32_t friend_id );
181
182
183/**
184 * @brief Hangup active call.
185 *
186 * @param session Control session.
187 * @return int
188 * @retval -1 Error occured.
189 * @retval 0 Success.
190 */
191int msi_hangup ( MSISession* session );
192
193
194/**
195 * @brief Answer active call request.
196 *
197 * @param session Control session.
198 * @param call_type Answer with Audio or Video(both).
199 * @return int
200 */
201int msi_answer ( MSISession* session, MSICallType call_type );
202
203
204/**
205 * @brief Cancel request.
206 *
207 * @param session Control session.
208 * @param peer To which peer.
209 * @param reason Set optional reason header. Pass NULL if none.
210 * @return int
211 */
212int msi_cancel ( MSISession* session, uint32_t peer, const uint8_t* reason );
213
214
215/**
216 * @brief Reject request.
217 *
218 * @param session Control session.
219 * @param reason Set optional reason header. Pass NULL if none.
220 * @return int
221 */
222int msi_reject ( MSISession* session, const uint8_t* reason );
223
224
225/**
226 * @brief Terminate the current call.
227 *
228 * @param session Control session.
229 * @return int
230 */
231int msi_stopcall ( MSISession* session );
232
233#endif /* __TOXMSI */
diff --git a/toxav/phone.c b/toxav/phone.c
new file mode 100755
index 00000000..161275d9
--- /dev/null
+++ b/toxav/phone.c
@@ -0,0 +1,1308 @@
1/** phone.c
2 *
3 * NOTE NOTE NOTE NOTE NOTE NOTE
4 *
5 * This file is for testing/reference purposes only, hence
6 * it is _poorly_ designed and it does not fully reflect the
7 * quaility of msi nor rtp. Although toxmsi* and toxrtp* are tested
8 * there is always possiblity of crashes. If crash occures,
9 * contact me ( mannol ) on either irc channel #tox-dev @ freenode.net:6667
10 * or eniz_vukovic@hotmail.com
11 *
12 * NOTE NOTE NOTE NOTE NOTE NOTE
13 *
14 * Copyright (C) 2013 Tox project All Rights Reserved.
15 *
16 * This file is part of Tox.
17 *
18 * Tox is free software: you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation, either version 3 of the License, or
21 * (at your option) any later version.
22 *
23 * Tox is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with Tox. If not, see <http://www.gnu.org/licenses/>.
30 *
31 */
32
33#ifdef HAVE_CONFIG_H
34#include "config.h"
35#endif /* HAVE_CONFIG_H */
36
37#define _BSD_SOURCE
38
39#include <stdio.h>
40#include <string.h>
41#include <stdlib.h>
42#include <stdarg.h>
43#include <unistd.h>
44#include <assert.h>
45#include <math.h>
46#include <pthread.h>
47
48//#include "media.h"
49#include "toxav.h"
50#include "event.h"
51#include "../toxcore/tox.h"
52
53#ifdef TOX_FFMPEG
54/* Video encoding/decoding */
55#include <libavcodec/avcodec.h>
56#include <libavformat/avformat.h>
57#include <libswscale/swscale.h>
58#include <libavdevice/avdevice.h>
59#include <libavutil/opt.h>
60#endif
61
62#include <AL/al.h>
63#include <AL/alc.h>
64#include <SDL/SDL.h>
65#include <SDL/SDL_thread.h>
66
67/* the quit event for SDL */
68#define FF_QUIT_EVENT (SDL_USEREVENT + 2)
69
70#ifdef __linux__
71#define VIDEO_DRIVER "video4linux2"
72#define DEFAULT_WEBCAM "/dev/video0"
73#endif
74
75#if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
76#define VIDEO_DRIVER "vfwcap"
77#define DEFAULT_WEBCAM "0"
78#endif
79
80
81/* Define client version */
82#define _USERAGENT "v.0.3.0"
83
84
85struct SDL_Surface *screen;
86
87typedef struct {
88 struct SDL_Overlay *bmp;
89 int width, height;
90} VideoPicture;
91
92
93typedef struct av_friend_s {
94 int _id;
95 int _active; /* 0=false; 1=true; */
96} av_friend_t;
97
98typedef struct av_session_s {
99 /* Encoding/decoding/capturing/playing */
100 ToxAv* av;
101
102 VideoPicture video_picture;
103 struct ALCdevice *audio_capture_device;
104
105 /* context for converting image format to something SDL can use*/
106 struct SwsContext *sws_SDL_r_ctx;
107
108 /* context for converting webcam image format to something the video encoder can use */
109 struct SwsContext *sws_ctx;
110
111 /* Thread running control */
112 int running_decaud, running_encaud,
113 running_decvid, running_encvid;
114
115 pthread_mutex_t _mutex;
116
117 Tox* _messenger;
118 av_friend_t* _friends;
119 int _friend_cout;
120 char _my_public_id[200];
121#ifdef TOX_FFMPEG
122 AVInputFormat *video_input_format;
123 AVFormatContext *video_format_ctx;
124 uint8_t video_stream;
125 AVCodecContext *webcam_decoder_ctx;
126 AVCodec *webcam_decoder;
127#endif
128} av_session_t;
129
130
131void av_allocate_friend(av_session_t* _phone, int _id, int _active)
132{
133 static int _new_id = 0;
134
135 if ( !_phone->_friends ) {
136 _phone->_friends = calloc(sizeof(av_friend_t), 1);
137 _phone->_friend_cout = 1;
138 } else{
139 _phone->_friend_cout ++;
140 _phone->_friends = realloc(_phone->_friends, sizeof(av_friend_t) * _phone->_friend_cout);
141 }
142
143 if ( _id == -1 ) {
144 _phone->_friends->_id = _new_id;
145 _new_id ++;
146 } else _phone->_friends->_id = _id;
147
148 _phone->_friends->_active = _active;
149}
150av_friend_t* av_get_friend(av_session_t* _phone, int _id)
151{
152 av_friend_t* _friends = _phone->_friends;
153
154 if ( !_friends ) return NULL;
155
156 int _it = 0;
157 for (; _it < _phone->_friend_cout; _it ++)
158 if ( _friends[_it]._id == _id )
159 return _friends + _it;
160
161 return NULL;
162}
163
164
165/***************** MISC *****************/
166
167void INFO (const char* _format, ...)
168{
169 printf("\r[!] ");
170 va_list _arg;
171 va_start (_arg, _format);
172 vfprintf (stdout, _format, _arg);
173 va_end (_arg);
174 printf("\n\r >> ");
175 fflush(stdout);
176}
177
178unsigned char *hex_string_to_bin(char hex_string[])
179{
180 size_t i, len = strlen(hex_string);
181 unsigned char *val = calloc(sizeof(unsigned char), len);
182 char *pos = hex_string;
183
184 for (i = 0; i < len; ++i, pos += 2)
185 sscanf(pos, "%2hhx", &val[i]);
186
187 return val;
188}
189
190int getinput( char* _buff, size_t _limit, int* _len )
191{
192 if ( fgets(_buff, _limit, stdin) == NULL )
193 return -1;
194
195 *_len = strlen(_buff) - 1;
196
197 /* Get rid of newline */
198 _buff[*_len] = '\0';
199
200 return 0;
201}
202
203char* trim_spaces ( char* buff )
204{
205
206 int _i = 0, _len = strlen(buff);
207
208 char* container = calloc(sizeof(char), _len);
209 int _ci = 0;
210
211 for ( ; _i < _len; _i++ ) {
212 while ( _i < _len && buff[_i] == ' ' )
213 _i++;
214
215 if ( _i < _len ){
216 container[_ci] = buff[_i];
217 _ci ++;
218 }
219 }
220
221 memcpy( buff, container, _ci );
222 buff[_ci] = '\0';
223 free(container);
224 return buff;
225}
226
227#define FRADDR_TOSTR_CHUNK_LEN 8
228
229static void fraddr_to_str(uint8_t *id_bin, char *id_str)
230{
231 uint i, delta = 0, pos_extra = 0, sum_extra = 0;
232
233 for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
234 sprintf(&id_str[2 * i + delta], "%02hhX", id_bin[i]);
235
236 if ((i + 1) == TOX_CLIENT_ID_SIZE)
237 pos_extra = 2 * (i + 1) + delta;
238
239 if (i >= TOX_CLIENT_ID_SIZE)
240 sum_extra |= id_bin[i];
241
242 if (!((i + 1) % FRADDR_TOSTR_CHUNK_LEN)) {
243 id_str[2 * (i + 1) + delta] = ' ';
244 delta++;
245 }
246 }
247
248 id_str[2 * i + delta] = 0;
249
250 if (!sum_extra)
251 id_str[pos_extra] = 0;
252}
253
254/*********************************************
255 *********************************************
256 *********************************************
257 *********************************************
258 *********************************************
259 *********************************************
260 *********************************************
261 *********************************************
262 */
263
264
265/*
266 * How av stuff _should_ look like
267 */
268/*
269int display_received_frame(av_session_t* _phone, vpx_image_t *image)
270{
271 CodecState* cs = get_cs_temp(_phone->av);
272 AVPicture pict;
273 SDL_LockYUVOverlay(_phone->video_picture.bmp);
274
275 pict.data[0] = _phone->video_picture.bmp->pixels[0];
276 pict.data[1] = _phone->video_picture.bmp->pixels[2];
277 pict.data[2] = _phone->video_picture.bmp->pixels[1];
278 pict.linesize[0] = _phone->video_picture.bmp->pitches[0];
279 pict.linesize[1] = _phone->video_picture.bmp->pitches[2];
280 pict.linesize[2] = _phone->video_picture.bmp->pitches[1];
281 */
282 /* Convert the image into YUV format that SDL uses *//*
283 sws_scale(_phone->sws_SDL_r_ctx, (uint8_t const * const *)r_video_frame->data, r_video_frame->linesize, 0,
284 cs->video_decoder_ctx->height, pict.data, pict.linesize );
285
286 SDL_UnlockYUVOverlay(_phone->video_picture.bmp);
287 SDL_Rect rect;
288 rect.x = 0;
289 rect.y = 0;
290 rect.w = cs->video_decoder_ctx->width;
291 rect.h = cs->video_decoder_ctx->height;
292 SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect);
293 return 1;
294}
295*/
296#ifdef TOX_FFMPEG
297void *encode_video_thread(void *arg)
298{
299 INFO("Started encode video thread!");
300
301 av_session_t* _phone = arg;
302
303 _phone->running_encvid = 1;
304 //CodecState *cs = get_cs_temp(_phone->av);
305 AVPacket pkt1, *packet = &pkt1;
306 //int p = 0;
307 //int got_packet;
308 int video_frame_finished;
309 AVFrame *s_video_frame;
310 AVFrame *webcam_frame;
311 s_video_frame = avcodec_alloc_frame();
312 webcam_frame = avcodec_alloc_frame();
313 //AVPacket enc_video_packet;
314
315 uint8_t *buffer;
316 int numBytes;
317 /* Determine required buffer size and allocate buffer */
318 numBytes = avpicture_get_size(PIX_FMT_YUV420P, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height);
319 buffer = (uint8_t *)av_calloc(numBytes * sizeof(uint8_t),1);
320 avpicture_fill((AVPicture *)s_video_frame, buffer, PIX_FMT_YUV420P, _phone->webcam_decoder_ctx->width,
321 _phone->webcam_decoder_ctx->height);
322 _phone->sws_ctx = sws_getContext(_phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height,
323 _phone->webcam_decoder_ctx->pix_fmt, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height, PIX_FMT_YUV420P,
324 SWS_BILINEAR, NULL, NULL, NULL);
325
326
327 vpx_image_t *image =
328 vpx_img_alloc(NULL, VPX_IMG_FMT_I420, _phone->webcam_decoder_ctx->width, _phone->webcam_decoder_ctx->height, 1);
329
330 //uint32_t frame_counter = 0;
331 while (_phone->running_encvid) {
332
333 if (av_read_frame(_phone->video_format_ctx, packet) < 0) {
334 printf("error reading frame\n");
335
336 if (_phone->video_format_ctx->pb->error != 0)
337 break;
338
339 continue;
340 }
341
342 if (packet->stream_index == _phone->video_stream) {
343 if (avcodec_decode_video2(_phone->webcam_decoder_ctx, webcam_frame, &video_frame_finished, packet) < 0) {
344 printf("couldn't decode\n");
345 continue;
346 }
347
348 av_free_packet(packet);
349 sws_scale(_phone->sws_ctx, (uint8_t const * const *)webcam_frame->data, webcam_frame->linesize, 0,
350 _phone->webcam_decoder_ctx->height, s_video_frame->data, s_video_frame->linesize);
351 /* create a new I-frame every 60 frames */
352 //++p;
353 /*
354 if (p == 60) {
355
356 s_video_frame->pict_type = AV_PICTURE_TYPE_BI ;
357 } else if (p == 61) {
358 s_video_frame->pict_type = AV_PICTURE_TYPE_I ;
359 p = 0;
360 } else {
361 s_video_frame->pict_type = AV_PICTURE_TYPE_P ;
362 }*/
363
364 if (video_frame_finished) {
365 memcpy(image->planes[VPX_PLANE_Y], s_video_frame->data[0], s_video_frame->linesize[0] * _phone->webcam_decoder_ctx->height);
366 memcpy(image->planes[VPX_PLANE_U], s_video_frame->data[1], s_video_frame->linesize[1] * _phone->webcam_decoder_ctx->height / 2);
367 memcpy(image->planes[VPX_PLANE_V], s_video_frame->data[2], s_video_frame->linesize[2] * _phone->webcam_decoder_ctx->height / 2);
368 toxav_send_video (_phone->av, image);
369 //if (avcodec_encode_video2(cs->video_encoder_ctx, &enc_video_packet, s_video_frame, &got_packet) < 0) {
370 /*if (vpx_codec_encode(&cs->v_encoder, image, frame_counter, 1, 0, 0) != VPX_CODEC_OK) {
371 printf("could not encode video frame\n");
372 continue;
373 }
374 ++frame_counter;
375
376 vpx_codec_iter_t iter = NULL;
377 vpx_codec_cx_pkt_t *pkt;
378 while( (pkt = vpx_codec_get_cx_data(&cs->v_encoder, &iter)) ) {
379 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT)
380 toxav_send_rtp_payload(_phone->av, TypeVideo, pkt->data.frame.buf, pkt->data.frame.sz);
381 }*/
382 //if (!got_packet) {
383 // continue;
384 //}
385
386 //if (!enc_video_packet.data) fprintf(stderr, "video packet data is NULL\n");
387
388 //toxav_send_rtp_payload(_phone->av, TypeVideo, enc_video_packet.data, enc_video_packet.size);
389
390 //av_free_packet(&enc_video_packet);
391 }
392 } else {
393 av_free_packet(packet);
394 }
395 }
396
397 vpx_img_free(image);
398
399 /* clean up codecs */
400 //pthread_mutex_lock(&cs->ctrl_mutex);
401 av_free(buffer);
402 av_free(webcam_frame);
403 av_free(s_video_frame);
404 sws_freeContext(_phone->sws_ctx);
405 //avcodec_close(webcam_decoder_ctx);
406 //avcodec_close(cs->video_encoder_ctx);
407 //pthread_mutex_unlock(&cs->ctrl_mutex);
408
409 _phone->running_encvid = -1;
410
411 pthread_exit ( NULL );
412}
413#endif
414
415void *encode_audio_thread(void *arg)
416{
417 INFO("Started encode audio thread!");
418 av_session_t* _phone = arg;
419 _phone->running_encaud = 1;
420
421 int ret = 0;
422 int16_t frame[4096];
423 int frame_size = AUDIO_FRAME_SIZE;
424 ALint sample = 0;
425 alcCaptureStart((ALCdevice*)_phone->audio_capture_device);
426
427 while (_phone->running_encaud) {
428 alcGetIntegerv((ALCdevice*)_phone->audio_capture_device, ALC_CAPTURE_SAMPLES, (ALCsizei)sizeof(ALint), &sample);
429
430 if (sample >= frame_size) {
431 alcCaptureSamples((ALCdevice*)_phone->audio_capture_device, frame, frame_size);
432
433 ret = toxav_send_audio(_phone->av, frame, frame_size);
434
435 if (ret < 0)
436 printf("Could not encode or send audio packet\n");
437
438 } else {
439 usleep(1000);
440 }
441 }
442
443 /* clean up codecs *
444 pthread_mutex_lock(&cs->ctrl_mutex);* /
445 alcCaptureStop((ALCdevice*)_phone->audio_capture_device);
446 alcCaptureCloseDevice((ALCdevice*)_phone->audio_capture_device);
447 / *pthread_mutex_unlock(&cs->ctrl_mutex);*/
448 _phone->running_encaud = -1;
449 pthread_exit ( NULL );
450}
451
452void convert_to_rgb(vpx_image_t *img, unsigned char *out)
453{
454 const int w = img->d_w;
455 const int w2 = w/2;
456 const int pstride = w*3;
457 const int h = img->d_h;
458 const int h2 = h/2;
459
460 const int strideY = img->stride[0];
461 const int strideU = img->stride[1];
462 const int strideV = img->stride[2];
463 int posy, posx;
464 for (posy = 0; posy < h2; posy++) {
465 unsigned char *dst = out + pstride * (posy * 2);
466 unsigned char *dst2 = out + pstride * (posy * 2 + 1);
467 const unsigned char *srcY = img->planes[0] + strideY * posy * 2;
468 const unsigned char *srcY2 = img->planes[0] + strideY * (posy * 2 + 1);
469 const unsigned char *srcU = img->planes[1] + strideU * posy;
470 const unsigned char *srcV = img->planes[2] + strideV * posy;
471
472 for (posx = 0; posx < w2; posx++) {
473 unsigned char Y,U,V;
474 short R,G,B;
475 short iR,iG,iB;
476
477 U = *(srcU++); V = *(srcV++);
478 iR = (351 * (V-128)) / 256;
479 iG = - (179 * (V-128)) / 256 - (86 * (U-128)) / 256;
480 iB = (444 * (U-128)) / 256;
481
482 Y = *(srcY++);
483 R = Y + iR ; G = Y + iG ; B = Y + iB ;
484 R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
485 *(dst++) = R; *(dst++) = G; *(dst++) = B;
486
487 Y = *(srcY2++);
488 R = Y + iR ; G = Y + iG ; B = Y + iB ;
489 R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
490 *(dst2++) = R; *(dst2++) = G; *(dst2++) = B;
491
492 Y = *(srcY++) ;
493 R = Y + iR ; G = Y + iG ; B = Y + iB ;
494 R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
495 *(dst++) = R; *(dst++) = G; *(dst++) = B;
496
497 Y = *(srcY2++);
498 R = Y + iR ; G = Y + iG ; B = Y + iB ;
499 R = (R<0?0:(R>255?255:R)); G = (G<0?0:(G>255?255:G)); B = (B<0?0:(B>255?255:B));
500 *(dst2++) = R; *(dst2++) = G; *(dst2++) = B;
501 }
502 }
503}
504
505#define mask32(BYTE) (*(uint32_t *)(uint8_t [4]){ [BYTE] = 0xff })
506
507void *decode_video_thread(void *arg)
508{
509 INFO("Started decode video thread!");
510 av_session_t* _phone = arg;
511 _phone->running_decvid = 1;
512
513 //CodecState *cs = get_cs_temp(_phone->av);
514 //cs->video_stream = 0;
515
516 //int recved_size;
517 //uint8_t dest[RTP_PAYLOAD_SIZE];
518
519 //int dec_frame_finished;
520 //AVFrame *r_video_frame;
521 //r_video_frame = avcodec_alloc_frame();
522 //AVPacket dec_video_packet;
523 //av_new_packet (&dec_video_packet, 65536);
524 int width = 0;
525 int height = 0;
526
527 while (_phone->running_decvid) {
528 //recved_size = toxav_recv_rtp_payload(_phone->av, TypeVideo, dest);
529 //if (recved_size) {
530 vpx_image_t *image;
531 if (toxav_recv_video(_phone->av, &image) == 0) {
532 //memcpy(dec_video_packet.data, dest, recved_size);
533 //dec_video_packet.size = recved_size;
534
535 //avcodec_decode_video2(cs->video_decoder_ctx, r_video_frame, &dec_frame_finished, &dec_video_packet);
536
537 //if (dec_frame_finished) {
538
539 /* Check if size has changed */
540 if (image->d_w != width || image->d_h != height) {
541
542 width = image->d_w;
543 height = image->d_h;
544
545 printf("w: %d h: %d \n", width, height);
546
547 screen = SDL_SetVideoMode(width, height, 0, 0);
548
549 //if (_phone->video_picture.bmp)
550 // SDL_FreeYUVOverlay(_phone->video_picture.bmp);
551
552 //_phone->video_picture.bmp = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, screen);
553 // _phone->sws_SDL_r_ctx = sws_getContext(width, height, cs->video_decoder_ctx->pix_fmt, width, height, PIX_FMT_YUV420P,
554 // SWS_BILINEAR, NULL, NULL, NULL);
555 }
556 uint8_t *rgb_image = malloc(width*height*3);
557 convert_to_rgb(image, rgb_image);
558 SDL_Surface* img_surface = SDL_CreateRGBSurfaceFrom(rgb_image, width, height, 24, width * 3, mask32(0), mask32(1), mask32(2), 0);
559 if(SDL_BlitSurface(img_surface, NULL, screen, NULL) == 0)
560 SDL_UpdateRect(screen, 0, 0, 0, 0);
561 /*
562 SDL_LockYUVOverlay(_phone->video_picture.bmp);
563 memcpy(_phone->video_picture.bmp->pixels[0], image->planes[VPX_PLANE_Y], _phone->video_picture.bmp->pitches[0] * height);
564 memcpy(_phone->video_picture.bmp->pixels[1], image->planes[VPX_PLANE_V], _phone->video_picture.bmp->pitches[1] * height / 2);
565 memcpy(_phone->video_picture.bmp->pixels[2], image->planes[VPX_PLANE_U], _phone->video_picture.bmp->pitches[2] * height / 2);
566
567 SDL_Rect rect;
568 rect.x = 0;
569 rect.y = 0;
570 rect.w = width;
571 rect.h = height;
572 SDL_DisplayYUVOverlay(_phone->video_picture.bmp, &rect);*/
573 free(rgb_image);
574 //display_received_frame(_phone, image);
575
576 } //else {
577 /* TODO: request the sender to create a new i-frame immediatly */
578 //printf("Bad video packet\n");
579 //}
580 //}
581
582 usleep(1000);
583 }
584
585 /* clean up codecs */
586 //av_free(r_video_frame);
587
588 //pthread_mutex_lock(&cs->ctrl_mutex);
589 //avcodec_close(cs->video_decoder_ctx);
590 //pthread_mutex_unlock(&cs->ctrl_mutex);
591
592 _phone->running_decvid = -1;
593
594 pthread_exit ( NULL );
595}
596
597void *decode_audio_thread(void *arg)
598{
599 INFO("Started decode audio thread!");
600 av_session_t* _phone = arg;
601 _phone->running_decaud = 1;
602
603 //int recved_size;
604 //uint8_t dest [RTP_PAYLOAD_SIZE];
605
606 int frame_size = AUDIO_FRAME_SIZE;
607 //int data_size;
608
609 ALCdevice *dev;
610 ALCcontext *ctx;
611 ALuint source, *buffers;
612 dev = alcOpenDevice(NULL);
613 ctx = alcCreateContext(dev, NULL);
614 alcMakeContextCurrent(ctx);
615 int openal_buffers = 5;
616
617 buffers = calloc(sizeof(ALuint) * openal_buffers,1);
618 alGenBuffers(openal_buffers, buffers);
619 alGenSources((ALuint)1, &source);
620 alSourcei(source, AL_LOOPING, AL_FALSE);
621
622 ALuint buffer;
623 ALint ready;
624
625 uint16_t zeros[frame_size];
626 memset(zeros, 0, frame_size);
627 int16_t PCM[frame_size];
628
629 int i;
630 for (i = 0; i < openal_buffers; ++i) {
631 alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, frame_size, 48000);
632 }
633
634 alSourceQueueBuffers(source, openal_buffers, buffers);
635 alSourcePlay(source);
636
637 if (alGetError() != AL_NO_ERROR) {
638 fprintf(stderr, "Error starting audio\n");
639 goto ending;
640 }
641
642 int dec_frame_len = 0;
643
644 while (_phone->running_decaud) {
645
646 alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready);
647 if (ready <= 0)
648 continue;
649
650 dec_frame_len = toxav_recv_audio(_phone->av, frame_size, PCM);
651
652 /* Play the packet */
653 if (dec_frame_len > 0) {
654 alSourceUnqueueBuffers(source, 1, &buffer);
655 alBufferData(buffer, AL_FORMAT_MONO16, PCM, dec_frame_len * 2 * 1, 48000);
656 int error = alGetError();
657
658 if (error != AL_NO_ERROR) {
659 fprintf(stderr, "Error setting buffer %d\n", error);
660 break;
661 }
662
663 alSourceQueueBuffers(source, 1, &buffer);
664
665 if (alGetError() != AL_NO_ERROR) {
666 fprintf(stderr, "Error: could not buffer audio\n");
667 break;
668 }
669
670 alGetSourcei(source, AL_SOURCE_STATE, &ready);
671
672 if (ready != AL_PLAYING) alSourcePlay(source);
673 }
674
675 usleep(1000);
676 }
677
678
679ending:
680 /* clean up codecs */
681 //pthread_mutex_lock(&cs->ctrl_mutex);
682 /*
683 alDeleteSources(1, &source);
684 alDeleteBuffers(openal_buffers, buffers);
685 alcMakeContextCurrent(NULL);
686 alcDestroyContext(ctx);
687 alcCloseDevice(dev);
688 */
689 //pthread_mutex_unlock(&cs->ctrl_mutex);
690
691 _phone->running_decaud = -1;
692
693 pthread_exit ( NULL );
694}
695
696
697
698
699
700int phone_startmedia_loop ( ToxAv* arg )
701{
702 if ( !arg ){
703 return -1;
704 }
705
706 toxav_prepare_transmission(arg);
707
708 /*
709 * Rise all threads
710 */
711#ifdef TOX_FFMPEG
712 /* Only checks for last peer */
713 if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo &&
714 0 > event.rise(encode_video_thread, toxav_get_agent_handler(arg)) )
715 {
716 INFO("Error while starting encode_video_thread()");
717 return -1;
718 }
719#endif
720 /* Always send audio */
721 if ( 0 > event.rise(encode_audio_thread, toxav_get_agent_handler(arg)) )
722 {
723 INFO("Error while starting encode_audio_thread()");
724 return -1;
725 }
726
727 /* Only checks for last peer */
728 if ( toxav_get_peer_transmission_type(arg, 0) == TypeVideo &&
729 0 > event.rise(decode_video_thread, toxav_get_agent_handler(arg)) )
730 {
731 INFO("Error while starting decode_video_thread()");
732 return -1;
733 }
734
735 if ( 0 > event.rise(decode_audio_thread, toxav_get_agent_handler(arg)) )
736 {
737 INFO("Error while starting decode_audio_thread()");
738 return -1;
739 }
740
741
742 return 0;
743}
744
745
746
747
748
749
750/*********************************************
751 *********************************************
752 *********************************************
753 *********************************************
754 *********************************************
755 *********************************************
756 *********************************************
757 *********************************************
758 */
759
760
761/* Some example callbacks */
762
763void* callback_recv_invite ( void* _arg )
764{
765 assert(_arg);
766
767 switch ( toxav_get_peer_transmission_type(_arg, 0) ){
768 case TypeAudio:
769 INFO( "Incoming audio call!");
770 break;
771 case TypeVideo:
772 INFO( "Incoming video call!");
773 break;
774 }
775
776 pthread_exit(NULL);
777}
778void* callback_recv_ringing ( void* _arg )
779{
780 INFO ( "Ringing!" );
781 pthread_exit(NULL);
782}
783void* callback_recv_starting ( void* _arg )
784{
785 if ( 0 != phone_startmedia_loop(_arg) ){
786 INFO("Starting call failed!");
787 } else {
788 INFO ("Call started! ( press h to hangup )");
789 }
790 pthread_exit(NULL);
791}
792void* callback_recv_ending ( void* _arg )
793{
794 av_session_t* _phone = toxav_get_agent_handler(_arg);
795
796 _phone->running_encaud = 0;
797 _phone->running_decaud = 0;
798 _phone->running_encvid = 0;
799 _phone->running_decvid = 0;
800
801 /* Wait until all threads are done */
802
803 while ( _phone->running_encaud != -1 ||
804 _phone->running_decaud != -1 ||
805 _phone->running_encvid != -1 ||
806 _phone->running_decvid != -1 )
807
808 usleep(10000000);
809
810 INFO ( "Call ended!" );
811 pthread_exit(NULL);
812}
813
814void* callback_recv_error ( void* _arg )
815{
816 /*MSISession* _session = _arg;
817
818 INFO( "Error: %s", _session->last_error_str ); */
819 pthread_exit(NULL);
820}
821
822void* callback_call_started ( void* _arg )
823{
824 if ( 0 != phone_startmedia_loop(_arg) ){
825 INFO("Starting call failed!");
826 } else {
827 INFO ("Call started! ( press h to hangup )");
828 }
829
830 pthread_exit(NULL);
831}
832void* callback_call_canceled ( void* _arg )
833{
834 INFO ( "Call canceled!" );
835 pthread_exit(NULL);
836}
837void* callback_call_rejected ( void* _arg )
838{
839 INFO ( "Call rejected!" );
840 pthread_exit(NULL);
841}
842void* callback_call_ended ( void* _arg )
843{
844 av_session_t* _phone = toxav_get_agent_handler(_arg);
845
846 _phone->running_encaud = 0;
847 _phone->running_decaud = 0;
848 _phone->running_encvid = 0;
849 _phone->running_decvid = 0;
850
851 /* Wait until all threads are done */
852
853 while ( _phone->running_encaud != -1 ||
854 _phone->running_decaud != -1 ||
855 _phone->running_encvid != -1 ||
856 _phone->running_decvid != -1 )
857
858 usleep(10000000);
859
860 toxav_kill_transmission(_phone->av);
861 INFO ( "Call ended!" );
862 pthread_exit(NULL);
863}
864
865void* callback_requ_timeout ( void* _arg )
866{
867 INFO( "No answer! " );
868 pthread_exit(NULL);
869}
870
871av_session_t* av_init_session()
872{
873 av_session_t* _retu = malloc(sizeof(av_session_t));
874
875 /* Initialize our mutex */
876 pthread_mutex_init ( &_retu->_mutex, NULL );
877
878 _retu->_messenger = tox_new(1);
879
880 if ( !_retu->_messenger ) {
881 fprintf ( stderr, "tox_new() failed!\n" );
882 return NULL;
883 }
884
885 _retu->_friends = NULL;
886
887
888 const ALchar *_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
889 int i = 0;
890 const ALchar *device_names[20];
891
892 if ( _device_list ) {
893 INFO("\nAvailable Capture Devices are:");
894
895 while (*_device_list ) {
896 device_names[i] = _device_list;
897 INFO("%d) %s", i, device_names[i]);
898 _device_list += strlen( _device_list ) + 1;
899 ++i;
900 }
901 }
902
903 INFO("Enter capture device number");
904
905 char dev[2]; char* left;
906 char* warned_ = fgets(dev, 2, stdin);
907 (void)warned_;
908 long selection = strtol(dev, &left, 10);
909
910 if ( *left ) {
911 printf("'%s' is not a number!", dev);
912 fflush(stdout);
913 exit(EXIT_FAILURE);
914 }
915 else {
916 INFO("Selected: %d ( %s )", selection, device_names[selection]);
917 }
918
919 _retu->audio_capture_device =
920 (struct ALCdevice*)alcCaptureOpenDevice(
921 device_names[selection], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4);
922
923
924 if (alcGetError((ALCdevice*)_retu->audio_capture_device) != AL_NO_ERROR) {
925 printf("Could not start capture device! %d\n", alcGetError((ALCdevice*)_retu->audio_capture_device));
926 return 0;
927 }
928 uint16_t height = 0, width = 0;
929#ifdef TOX_FFMPEG
930 avdevice_register_all();
931 avcodec_register_all();
932 av_register_all();
933
934 _retu->video_input_format = av_find_input_format(VIDEO_DRIVER);
935 if (avformat_open_input(&_retu->video_format_ctx, DEFAULT_WEBCAM, _retu->video_input_format, NULL) != 0) {
936 fprintf(stderr, "Opening video_input_format failed!\n");
937 //return -1;
938 return NULL;
939 }
940
941 avformat_find_stream_info(_retu->video_format_ctx, NULL);
942 av_dump_format(_retu->video_format_ctx, 0, DEFAULT_WEBCAM, 0);
943
944 for (i = 0; i < _retu->video_format_ctx->nb_streams; ++i) {
945 if (_retu->video_format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
946 _retu->video_stream = i;
947 break;
948 }
949 }
950
951 _retu->webcam_decoder_ctx = _retu->video_format_ctx->streams[_retu->video_stream]->codec;
952 _retu->webcam_decoder = avcodec_find_decoder(_retu->webcam_decoder_ctx->codec_id);
953
954 if (_retu->webcam_decoder == NULL) {
955 fprintf(stderr, "Unsupported codec!\n");
956 //return -1;
957 return NULL;
958 }
959
960 if (_retu->webcam_decoder_ctx == NULL) {
961 fprintf(stderr, "Init webcam_decoder_ctx failed!\n");
962 //return -1;
963 return NULL;
964 }
965
966 if (avcodec_open2(_retu->webcam_decoder_ctx, _retu->webcam_decoder, NULL) < 0) {
967 fprintf(stderr, "Opening webcam decoder failed!\n");
968 //return -1;
969 return NULL;
970 }
971 width = _retu->webcam_decoder_ctx->width;
972 height = _retu->webcam_decoder_ctx->height;
973#endif
974 uint8_t _byte_address[TOX_FRIEND_ADDRESS_SIZE];
975 tox_get_address(_retu->_messenger, _byte_address );
976 fraddr_to_str( _byte_address, _retu->_my_public_id );
977
978
979 _retu->av = toxav_new(_retu->_messenger, _retu, _USERAGENT, width, height);
980
981 /* ------------------ */
982
983 toxav_register_callstate_callback(callback_call_started, OnStart);
984 toxav_register_callstate_callback(callback_call_canceled, OnCancel);
985 toxav_register_callstate_callback(callback_call_rejected, OnReject);
986 toxav_register_callstate_callback(callback_call_ended, OnEnd);
987 toxav_register_callstate_callback(callback_recv_invite, OnInvite);
988
989 toxav_register_callstate_callback(callback_recv_ringing, OnRinging);
990 toxav_register_callstate_callback(callback_recv_starting, OnStarting);
991 toxav_register_callstate_callback(callback_recv_ending, OnEnding);
992
993 toxav_register_callstate_callback(callback_recv_error, OnError);
994 toxav_register_callstate_callback(callback_requ_timeout, OnRequestTimeout);
995
996 /* ------------------ */
997
998 return _retu;
999}
1000
1001int av_terminate_session(av_session_t* _phone)
1002{
1003 toxav_hangup(_phone->av);
1004
1005 free(_phone->_friends);
1006 pthread_mutex_destroy ( &_phone->_mutex );
1007
1008 Tox* _p = _phone->_messenger;
1009 _phone->_messenger = NULL; usleep(100000); /* Wait for tox_poll to end */
1010
1011 tox_kill(_p);
1012 toxav_kill(_phone->av);
1013
1014 free(_phone);
1015
1016 printf("\r[i] Quit!\n");
1017 return 0;
1018}
1019
1020/****** AV HELPER FUNCTIONS ******/
1021
1022/* Auto accept friend request */
1023void av_friend_requ(uint8_t *_public_key, uint8_t *_data, uint16_t _length, void *_userdata)
1024{
1025 av_session_t* _phone = _userdata;
1026 av_allocate_friend (_phone, -1, 0);
1027
1028 INFO("Got friend request with message: %s", _data);
1029
1030 tox_add_friend_norequest(_phone->_messenger, _public_key);
1031
1032 INFO("Auto-accepted! Friend id: %d", _phone->_friends->_id );
1033}
1034
1035void av_friend_active(Tox *_messenger, int _friendnumber, uint8_t *_string, uint16_t _length, void *_userdata)
1036{
1037 av_session_t* _phone = _userdata;
1038 INFO("Friend no. %d is online", _friendnumber);
1039
1040 av_friend_t* _this_friend = av_get_friend(_phone, _friendnumber);
1041
1042 if ( !_this_friend ) {
1043 INFO("But it's not registered!");
1044 return;
1045 }
1046
1047 (*_this_friend)._active = 1;
1048}
1049
1050int av_add_friend(av_session_t* _phone, char* _friend_hash)
1051{
1052 trim_spaces(_friend_hash);
1053
1054 unsigned char *_bin_string = hex_string_to_bin(_friend_hash);
1055 int _number = tox_add_friend(_phone->_messenger, _bin_string, (uint8_t *)"Tox phone "_USERAGENT, sizeof("Tox phone "_USERAGENT));
1056 free(_bin_string);
1057
1058 if ( _number >= 0) {
1059 INFO("Added friend as %d", _number );
1060 av_allocate_friend(_phone, _number, 0);
1061 }
1062 else
1063 INFO("Unknown error %i", _number );
1064
1065 return _number;
1066}
1067
1068int av_connect_to_dht(av_session_t* _phone, char* _dht_key, const char* _dht_addr, unsigned short _dht_port)
1069{
1070 unsigned char *_binary_string = hex_string_to_bin(_dht_key);
1071
1072 uint16_t _port = htons(_dht_port);
1073
1074 int _if = tox_bootstrap_from_address(_phone->_messenger, _dht_addr, 1, _port, _binary_string );
1075
1076 free(_binary_string);
1077
1078 return _if ? 0 : -1;
1079}
1080
1081/*********************************/
1082
1083void do_phone ( av_session_t* _phone )
1084{
1085 INFO("Welcome to tox_phone version: " _USERAGENT "\n"
1086 "Usage: \n"
1087 "f [pubkey] (add friend)\n"
1088 "c [a/v] (type) [friend] (friend id) (calls friend if online)\n"
1089 "h (if call is active hang up)\n"
1090 "a [a/v] (answer incoming call: a - audio / v - audio + video (audio is default))\n"
1091 "r (reject incoming call)\n"
1092 "q (quit)\n"
1093 "================================================================================"
1094 );
1095
1096 while ( 1 )
1097 {
1098 char _line [ 1500 ];
1099 int _len;
1100
1101 if ( -1 == getinput(_line, 1500, &_len) ){
1102 printf(" >> ");
1103 fflush(stdout);
1104 continue;
1105 }
1106
1107 if ( _len > 1 && _line[1] != ' ' && _line[1] != '\n' ){
1108 INFO("Invalid input!");
1109 continue;
1110 }
1111
1112 switch (_line[0]){
1113
1114 case 'f':
1115 {
1116 char _id [128];
1117 strncpy(_id, _line + 2, 128);
1118
1119 av_add_friend(_phone, _id);
1120
1121 } break;
1122 case 'c':
1123 {
1124 ToxAvCallType _ctype;
1125
1126 if ( _len < 5 ){
1127 INFO("Invalid input; usage: c a/v [friend]");
1128 break;
1129 }
1130 else if ( _line[2] == 'a' || _line[2] != 'v' ){ /* default and audio */
1131 _ctype = TypeAudio;
1132 }
1133 else { /* video */
1134 _ctype = TypeVideo;
1135 }
1136
1137 char* _end;
1138 int _friend = strtol(_line + 4, &_end, 10);
1139
1140 if ( *_end ){
1141 INFO("Friend num has to be numerical value");
1142 break;
1143 }
1144
1145 if ( toxav_call(_phone->av, _friend, _ctype, 30) == ErrorAlreadyInCall ){
1146 INFO("Already in a call");
1147 break;
1148 }
1149 else INFO("Calling friend: %d!", _friend);
1150
1151 } break;
1152 case 'h':
1153 {
1154 if ( toxav_hangup(_phone->av) == ErrorNoCall ) {
1155 INFO("No call!");
1156 break;
1157 }
1158 else INFO("Hung up...");
1159
1160 } break;
1161 case 'a':
1162 {
1163 ToxAvError rc;
1164
1165 if ( _len > 1 && _line[2] == 'v' ) {
1166 rc = toxav_answer(_phone->av, TypeVideo);
1167 } else
1168 rc = toxav_answer(_phone->av, TypeAudio);
1169
1170 if ( rc == ErrorInvalidState ) {
1171 INFO("No call to answer!");
1172 }
1173
1174 } break;
1175 case 'r':
1176 {
1177 if ( toxav_reject(_phone->av, "User action") == ErrorInvalidState )
1178 INFO("No state to cancel!");
1179 else INFO("Call Rejected...");
1180
1181 } break;
1182 case 'q':
1183 {
1184 INFO("Quitting!");
1185 return;
1186 }
1187 case '\n':
1188 {
1189 }
1190 default:
1191 {
1192 } break;
1193
1194 }
1195
1196 }
1197}
1198
1199void* tox_poll (void* _messenger_p)
1200{
1201 Tox** _messenger = _messenger_p;
1202 while( *_messenger ) {
1203 tox_do(*_messenger);
1204 usleep(10000);
1205 }
1206
1207 pthread_exit(NULL);
1208}
1209
1210int av_wait_dht(av_session_t* _phone, int _wait_seconds, const char* _ip, char* _key, unsigned short _port)
1211{
1212 if ( !_wait_seconds )
1213 return -1;
1214
1215 int _waited = 0;
1216
1217 while( !tox_isconnected(_phone->_messenger) ) {
1218
1219 if ( -1 == av_connect_to_dht(_phone, _key, _ip, _port) )
1220 {
1221 INFO("Could not connect to: %s", _ip);
1222 av_terminate_session(_phone);
1223 return -1;
1224 }
1225
1226 if ( _waited >= _wait_seconds ) return 0;
1227
1228 printf(".");
1229 fflush(stdout);
1230
1231 _waited ++;
1232 usleep(1000000);
1233 }
1234
1235 int _r = _wait_seconds - _waited;
1236 return _r ? _r : 1;
1237}
1238/* ---------------------- */
1239
1240int print_help ( const char* _name )
1241{
1242 printf ( "Usage: %s [IP] [PORT] [KEY]\n"
1243 "\t[IP] (DHT ip)\n"
1244 "\t[PORT] (DHT port)\n"
1245 "\t[KEY] (DHT public key)\n"
1246 "P.S. Friends and key are stored in ./tox_phone.conf\n"
1247 ,_name );
1248 return 1;
1249}
1250
1251int main ( int argc, char* argv [] )
1252{
1253 if ( argc < 1 || argc < 4 )
1254 return print_help(argv[0]);
1255
1256 char* _convertable;
1257
1258
1259 const char* _ip = argv[1];
1260 char* _key = argv[3];
1261 unsigned short _port = strtol(argv[2], &_convertable, 10);
1262
1263 if ( *_convertable ) {
1264 printf("Invalid port: cannot convert string to long: %s", _convertable);
1265 return 1;
1266 }
1267
1268 av_session_t* _phone = av_init_session();
1269
1270 tox_callback_friend_request(_phone->_messenger, av_friend_requ, _phone);
1271 tox_callback_status_message(_phone->_messenger, av_friend_active, _phone);
1272
1273
1274 INFO("\r================================================================================\n"
1275 "[!] Trying dht@%s:%d"
1276 , _ip, _port);
1277
1278 /* Start tox protocol */
1279 event.rise( tox_poll, &_phone->_messenger );
1280
1281 /* Just clean one line */
1282 printf("\r \r");
1283 fflush(stdout);
1284
1285 int _r;
1286 int _wait_seconds = 5;
1287 for ( _r = 0; _r == 0; _r = av_wait_dht(_phone, _wait_seconds, _ip, _key, _port) ) _wait_seconds --;
1288
1289
1290 if ( -1 == _r ) {
1291 INFO("Error while connecting to dht: %s:%d", _ip, _port);
1292 av_terminate_session(_phone);
1293 return 1;
1294 }
1295
1296 INFO("CONNECTED!\n"
1297 "================================================================================\n"
1298 "%s\n"
1299 "================================================================================"
1300 , _phone->_my_public_id );
1301
1302
1303 do_phone (_phone);
1304
1305 av_terminate_session(_phone);
1306
1307 return 0;
1308}
diff --git a/toxav/rtp.c b/toxav/rtp.c
new file mode 100644
index 00000000..2543c509
--- /dev/null
+++ b/toxav/rtp.c
@@ -0,0 +1,908 @@
1/** toxrtp.c
2 *
3 * Copyright (C) 2013 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 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif /* HAVE_CONFIG_H */
27
28#include "rtp.h"
29#include <assert.h>
30#include <stdlib.h>
31
32
33#define PAYLOAD_ID_VALUE_OPUS 1
34#define PAYLOAD_ID_VALUE_VP8 2
35
36#define size_32 4
37
38#define inline__ inline __attribute__((always_inline))
39
40
41#define ADD_FLAG_VERSION(_h, _v) do { ( _h->flags ) &= 0x3F; ( _h->flags ) |= ( ( ( _v ) << 6 ) & 0xC0 ); } while(0)
42#define ADD_FLAG_PADDING(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xDF; ( _h->flags ) |= ( ( ( _v ) << 5 ) & 0x20 ); } while(0)
43#define ADD_FLAG_EXTENSION(_h, _v) do { if ( _v > 0 ) _v = 1; ( _h->flags ) &= 0xEF;( _h->flags ) |= ( ( ( _v ) << 4 ) & 0x10 ); } while(0)
44#define ADD_FLAG_CSRCC(_h, _v) do { ( _h->flags ) &= 0xF0; ( _h->flags ) |= ( ( _v ) & 0x0F ); } while(0)
45#define ADD_SETTING_MARKER(_h, _v) do { if ( _v > 1 ) _v = 1; ( _h->marker_payloadt ) &= 0x7F; ( _h->marker_payloadt ) |= ( ( ( _v ) << 7 ) /*& 0x80 */ ); } while(0)
46#define ADD_SETTING_PAYLOAD(_h, _v) do { if ( _v > 127 ) _v = 127; ( _h->marker_payloadt ) &= 0x80; ( _h->marker_payloadt ) |= ( ( _v ) /* & 0x7F */ ); } while(0)
47
48#define GET_FLAG_VERSION(_h) (( _h->flags & 0xd0 ) >> 6)
49#define GET_FLAG_PADDING(_h) (( _h->flags & 0x20 ) >> 5)
50#define GET_FLAG_EXTENSION(_h) (( _h->flags & 0x10 ) >> 4)
51#define GET_FLAG_CSRCC(_h) ( _h->flags & 0x0f )
52#define GET_SETTING_MARKER(_h) (( _h->marker_payloadt ) >> 7)
53#define GET_SETTING_PAYLOAD(_h) ((_h->marker_payloadt) & 0x7f)
54
55
56/**
57 * @brief Converts 4 bytes to uint32_t
58 *
59 * @param dest Where to convert
60 * @param bytes What bytes
61 * @return void
62 */
63inline__ void bytes_to_U32(uint32_t* dest, const uint8_t* bytes)
64{
65 *dest =
66#ifdef WORDS_BIGENDIAN
67 ( ( uint32_t ) * bytes ) |
68 ( ( uint32_t ) * ( bytes + 1 ) << 8 ) |
69 ( ( uint32_t ) * ( bytes + 2 ) << 16 ) |
70 ( ( uint32_t ) * ( bytes + 3 ) << 24 ) ;
71#else
72 ( ( uint32_t ) * bytes << 24 ) |
73 ( ( uint32_t ) * ( bytes + 1 ) << 16 ) |
74 ( ( uint32_t ) * ( bytes + 2 ) << 8 ) |
75 ( ( uint32_t ) * ( bytes + 3 ) ) ;
76#endif
77}
78
79/**
80 * @brief Converts 2 bytes to uint16_t
81 *
82 * @param dest Where to convert
83 * @param bytes What bytes
84 * @return void
85 */
86inline__ void bytes_to_U16(uint16_t* dest, const uint8_t* bytes)
87{
88 *dest =
89#ifdef WORDS_BIGENDIAN
90 ( ( uint16_t ) * bytes ) |
91 ( ( uint16_t ) * ( bytes + 1 ) << 8 );
92#else
93 ( ( uint16_t ) * bytes << 8 ) |
94 ( ( uint16_t ) * ( bytes + 1 ) );
95#endif
96}
97
98/**
99 * @brief Convert uint32_t to byte string of size 4
100 *
101 * @param dest Where to convert
102 * @param value The value
103 * @return void
104 */
105inline__ void U32_to_bytes(uint8_t* dest, uint32_t value)
106{
107#ifdef WORDS_BIGENDIAN
108 *(dest) = ( value );
109 *(dest + 1) = ( value >> 8 );
110 *(dest + 2) = ( value >> 16 );
111 *(dest + 3) = ( value >> 24 );
112#else
113 *(dest) = ( value >> 24 );
114 *(dest + 1) = ( value >> 16 );
115 *(dest + 2) = ( value >> 8 );
116 *(dest + 3) = ( value );
117#endif
118}
119
120/**
121 * @brief Convert uint16_t to byte string of size 2
122 *
123 * @param dest Where to convert
124 * @param value The value
125 * @return void
126 */
127inline__ void U16_to_bytes(uint8_t* dest, uint16_t value)
128{
129#ifdef WORDS_BIGENDIAN
130 *(dest) = ( value );
131 *(dest + 1) = ( value >> 8 );
132#else
133 *(dest) = ( value >> 8 );
134 *(dest + 1) = ( value );
135#endif
136}
137
138
139/**
140 * @brief Checks if message came in late.
141 *
142 * @param session Control session.
143 * @param msg The message.
144 * @return int
145 * @retval -1 The message came in order.
146 * @retval 0 The message came late.
147 */
148inline__ int check_late_message (RTPSession* session, RTPMessage* msg)
149{
150 /*
151 * Check Sequence number. If this new msg has lesser number then the session->rsequnum
152 * it shows that the message came in late. Also check timestamp to be 100% certain.
153 *
154 */
155 return ( msg->header->sequnum < session->rsequnum && msg->header->timestamp < session->timestamp ) ? 0 : -1;
156}
157
158
159/**
160 * @brief Increases nonce value by 'target'
161 *
162 * @param nonce The nonce
163 * @param target The target
164 * @return void
165 */
166inline__ void increase_nonce(uint8_t* nonce, uint16_t target)
167{
168 uint16_t _nonce_counter;
169
170 uint8_t _reverse_bytes[2];
171 _reverse_bytes[0] = nonce[crypto_box_NONCEBYTES - 1];
172 _reverse_bytes[1] = nonce[crypto_box_NONCEBYTES - 2];
173
174 bytes_to_U16(&_nonce_counter, _reverse_bytes );
175
176 /* Check overflow */
177 if (_nonce_counter > UINT16_MAX - target ) { /* 2 bytes are not long enough */
178 uint8_t _it = 3;
179 while ( _it <= crypto_box_NONCEBYTES ) _it += ++nonce[crypto_box_NONCEBYTES - _it] ? crypto_box_NONCEBYTES : 1;
180
181 _nonce_counter = _nonce_counter - (UINT16_MAX - target ); /* Assign the rest of it */
182 } else { /* Increase nonce */
183
184 _nonce_counter+= target;
185 }
186
187 /* Assign the last bytes */
188
189 U16_to_bytes( _reverse_bytes, _nonce_counter);
190 nonce [crypto_box_NONCEBYTES - 1] = _reverse_bytes[0];
191 nonce [crypto_box_NONCEBYTES - 2] = _reverse_bytes[1];
192
193}
194
195
196/**
197 * @brief Speaks for it self.
198 *
199 */
200static const uint32_t payload_table[] =
201{
202 8000, 8000, 8000, 8000, 8000, 8000, 16000, 8000, 8000, 8000, /* 0-9 */
203 44100, 44100, 0, 0, 90000, 8000, 11025, 22050, 0, 0, /* 10-19 */
204 0, 0, 0, 0, 0, 90000, 90000, 0, 90000, 0, /* 20-29 */
205 0, 90000, 90000, 90000, 90000, 0, 0, 0, 0, 0, /* 30-39 */
206 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40-49 */
207 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50-59 */
208 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60-69 */
209 PAYLOAD_ID_VALUE_OPUS, PAYLOAD_ID_VALUE_VP8, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-79 */
210 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80-89 */
211 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90-99 */
212 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 100-109 */
213 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 110-119 */
214 0, 0, 0, 0, 0, 0, 0, 0 /* 120-127 */
215};
216
217
218/**
219 * @brief Extracts header from payload.
220 *
221 * @param payload The payload.
222 * @param length The size of payload.
223 * @return RTPHeader* Extracted header.
224 * @retval NULL Error occurred while extracting header.
225 */
226RTPHeader* extract_header ( const uint8_t* payload, int length )
227{
228 if ( !payload || !length ) {
229 return NULL;
230 }
231
232 const uint8_t* _it = payload;
233
234 RTPHeader* _retu = calloc(1, sizeof (RTPHeader));
235 assert(_retu);
236
237 _retu->flags = *_it; ++_it;
238
239 /* This indicates if the first 2 bits are valid.
240 * Now it may happen that this is out of order but
241 * it cuts down chances of parsing some invalid value
242 */
243
244 if ( GET_FLAG_VERSION(_retu) != RTP_VERSION ){
245 /* Deallocate */
246 free(_retu);
247 return NULL;
248 }
249
250 /*
251 * Added a check for the size of the header little sooner so
252 * I don't need to parse the other stuff if it's bad
253 */
254 uint8_t _cc = GET_FLAG_CSRCC ( _retu );
255 uint32_t _length = 12 /* Minimum header len */ + ( _cc * 4 );
256
257 if ( length < _length ) {
258 /* Deallocate */
259 free(_retu);
260 return NULL;
261 }
262
263 if ( _cc > 0 ) {
264 _retu->csrc = calloc (_cc, sizeof (uint32_t));
265 assert(_retu->csrc);
266
267 } else { /* But this should not happen ever */
268 /* Deallocate */
269 free(_retu);
270 return NULL;
271 }
272
273
274 _retu->marker_payloadt = *_it; ++_it;
275 _retu->length = _length;
276
277
278 bytes_to_U32(&_retu->timestamp, _it); _it += 4;
279 bytes_to_U32(&_retu->ssrc, _it);
280
281 uint8_t _x;
282 for ( _x = 0; _x < _cc; _x++ ) {
283 _it += 4; bytes_to_U32(&(_retu->csrc[_x]), _it);
284 }
285
286 return _retu;
287}
288
289/**
290 * @brief Extracts external header from payload. Must be called AFTER extract_header()!
291 *
292 * @param payload The ITERATED payload.
293 * @param length The size of payload.
294 * @return RTPExtHeader* Extracted extension header.
295 * @retval NULL Error occurred while extracting extension header.
296 */
297RTPExtHeader* extract_ext_header ( const uint8_t* payload, uint16_t length )
298{
299 const uint8_t* _it = payload;
300
301 RTPExtHeader* _retu = calloc(1, sizeof (RTPExtHeader));
302 assert(_retu);
303
304 uint16_t _ext_length;
305 bytes_to_U16(&_ext_length, _it); _it += 2;
306
307
308 if ( length < ( _ext_length * sizeof(uint32_t) ) ) {
309 free(_retu);
310 return NULL;
311 }
312
313 _retu->length = _ext_length;
314 bytes_to_U16(&_retu->type, _it); _it += 2;
315
316 _retu->table = calloc(_ext_length, sizeof (uint32_t));
317 assert(_retu->table);
318
319 uint16_t _x;
320 for ( _x = 0; _x < _ext_length; _x++ ) {
321 _it += 4; bytes_to_U32(&(_retu->table[_x]), _it);
322 }
323
324 return _retu;
325}
326
327/**
328 * @brief Adds header to payload. Make sure _payload_ has enough space.
329 *
330 * @param header The header.
331 * @param payload The payload.
332 * @return uint8_t* Iterated position.
333 */
334uint8_t* add_header ( RTPHeader* header, uint8_t* payload )
335{
336 uint8_t _cc = GET_FLAG_CSRCC ( header );
337
338 uint8_t* _it = payload;
339
340
341 /* Add sequence number first */
342 U16_to_bytes(_it, header->sequnum); _it += 2;
343
344 *_it = header->flags; ++_it;
345 *_it = header->marker_payloadt; ++_it;
346
347
348 U32_to_bytes( _it, header->timestamp); _it+=4;
349 U32_to_bytes( _it, header->ssrc);
350
351 if ( header->csrc ) {
352 uint8_t _x;
353 for ( _x = 0; _x < _cc; _x++ ) {
354 _it+=4; U32_to_bytes( _it, header->csrc[_x]);
355 }
356 }
357
358 return _it + 4;
359}
360
361/**
362 * @brief Adds extension header to payload. Make sure _payload_ has enough space.
363 *
364 * @param header The header.
365 * @param payload The payload.
366 * @return uint8_t* Iterated position.
367 */
368uint8_t* add_ext_header ( RTPExtHeader* header, uint8_t* payload )
369{
370 uint8_t* _it = payload;
371
372 U16_to_bytes(_it, header->length); _it+=2;
373 U16_to_bytes(_it, header->type); _it-=2; /* Return to 0 position */
374
375 if ( header->table ) {
376 uint16_t _x;
377 for ( _x = 0; _x < header->length; _x++ ) {
378 _it+=4; U32_to_bytes(_it, header->table[_x]);
379 }
380 }
381
382 return _it + 4;
383}
384
385/**
386 * @brief Builds header from control session values.
387 *
388 * @param session Control session.
389 * @return RTPHeader* Created header.
390 */
391RTPHeader* build_header ( RTPSession* session )
392{
393 RTPHeader* _retu = calloc ( 1, sizeof (RTPHeader) );
394 assert(_retu);
395
396 ADD_FLAG_VERSION ( _retu, session->version );
397 ADD_FLAG_PADDING ( _retu, session->padding );
398 ADD_FLAG_EXTENSION ( _retu, session->extension );
399 ADD_FLAG_CSRCC ( _retu, session->cc );
400 ADD_SETTING_MARKER ( _retu, session->marker );
401 ADD_SETTING_PAYLOAD ( _retu, session->payload_type );
402
403 _retu->sequnum = session->sequnum;
404 _retu->timestamp = ((uint32_t)(current_time() / 1000)); /* micro to milli */
405 _retu->ssrc = session->ssrc;
406
407 if ( session->cc > 0 ) {
408 _retu->csrc = calloc(session->cc, sizeof (uint32_t));
409 assert(_retu->csrc);
410
411 int i;
412
413 for ( i = 0; i < session->cc; i++ ) {
414 _retu->csrc[i] = session->csrc[i];
415 }
416 } else {
417 _retu->csrc = NULL;
418 }
419
420 _retu->length = 12 /* Minimum header len */ + ( session->cc * size_32 );
421
422 return _retu;
423}
424
425
426/**
427 * @brief Parses data into RTPMessage struct. Stores headers separately from the payload data
428 * and so the length variable is set accordingly. _sequnum_ argument is
429 * passed by the handle_packet() since it's parsed already.
430 *
431 * @param session Control session.
432 * @param sequnum Sequence number that's parsed from payload in handle_packet()
433 * @param data Payload data.
434 * @param length Payload size.
435 * @return RTPMessage*
436 * @retval NULL Error occurred.
437 */
438RTPMessage* msg_parse ( uint16_t sequnum, const uint8_t* data, int length )
439{
440 RTPMessage* _retu = calloc(1, sizeof (RTPMessage));
441
442 _retu->header = extract_header ( data, length ); /* It allocates memory and all */
443
444 if ( !_retu->header ){
445 free(_retu);
446 return NULL;
447 }
448 _retu->header->sequnum = sequnum;
449
450 _retu->length = length - _retu->header->length;
451
452 uint16_t _from_pos = _retu->header->length - 2 /* Since sequ num is excluded */ ;
453
454
455 if ( GET_FLAG_EXTENSION ( _retu->header ) ) {
456 _retu->ext_header = extract_ext_header ( data + _from_pos, length );
457 if ( _retu->ext_header ){
458 _retu->length -= ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 );
459 _from_pos += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 );
460 } else { /* Error */
461 free (_retu->ext_header);
462 free (_retu->header);
463 free (_retu);
464 return NULL;
465 }
466 } else {
467 _retu->ext_header = NULL;
468 }
469
470 if ( length - _from_pos <= MAX_RTP_SIZE )
471 memcpy ( _retu->data, data + _from_pos, length - _from_pos );
472 else {
473 rtp_free_msg(NULL, _retu);
474 return NULL;
475 }
476 _retu->next = NULL;
477
478 return _retu;
479}
480
481/**
482 * @brief Callback for networking core.
483 *
484 * @param object RTPSession object.
485 * @param ip_port Where the message comes from.
486 * @param data Message data.
487 * @param length Message length.
488 * @return int
489 * @retval -1 Error occurred.
490 * @retval 0 Success.
491 */
492int rtp_handle_packet ( void* object, IP_Port ip_port, uint8_t* data, uint32_t length )
493{
494 RTPSession* _session = object;
495 RTPMessage* _msg;
496
497 if ( !_session || length < 13 ) /* 12 is the minimum length for rtp + desc. byte */
498 return -1;
499
500 uint8_t _plain[MAX_UDP_PACKET_SIZE];
501
502 uint16_t _sequnum;
503 bytes_to_U16(&_sequnum, data + 1);
504
505 /* Clculate the right nonce */
506 uint8_t _calculated[crypto_box_NONCEBYTES];
507 memcpy(_calculated, _session->decrypt_nonce, crypto_box_NONCEBYTES);
508 increase_nonce ( _calculated, _sequnum );
509
510 /* Decrypt message */
511 int _decrypted_length = decrypt_data_symmetric(
512 (uint8_t*)_session->decrypt_key, _calculated, data + 3, length - 3, _plain );
513
514 /* This packet is either not encrypted properly or late
515 */
516 if ( -1 == _decrypted_length ){
517
518 /* If this is the case, then the packet is most likely late.
519 * Try with old nonce cycle.
520 */
521 if ( _session->rsequnum < _sequnum ) {
522 _decrypted_length = decrypt_data_symmetric(
523 (uint8_t*)_session->decrypt_key, _session->nonce_cycle, data + 3, length - 3, _plain );
524
525 if ( !_decrypted_length ) return -1; /* This packet is not encrypted properly */
526
527 /* Otherwise, if decryption is ok with new cycle, set new cycle
528 */
529 } else {
530 increase_nonce ( _calculated, MAX_SEQU_NUM );
531 _decrypted_length = decrypt_data_symmetric(
532 (uint8_t*)_session->decrypt_key, _calculated, data + 3, length - 3, _plain );
533
534 if ( !_decrypted_length ) return -1; /* This is just an error */
535
536 /* A new cycle setting. */
537 memcpy(_session->nonce_cycle, _session->decrypt_nonce, crypto_box_NONCEBYTES);
538 memcpy(_session->decrypt_nonce, _calculated, crypto_box_NONCEBYTES);
539 }
540 }
541
542 _msg = msg_parse ( _sequnum, _plain, _decrypted_length );
543
544 if ( !_msg ) return -1;
545
546 /* Hopefully this goes well
547 * NOTE: Is this even used?
548 */
549 memcpy(&_msg->from, &ip_port, sizeof(IP_Port));
550
551 /* Check if message came in late */
552 if ( check_late_message(_session, _msg) < 0 ) { /* Not late */
553 _session->rsequnum = _msg->header->sequnum;
554 _session->timestamp = _msg->header->timestamp;
555 }
556
557 pthread_mutex_lock(&_session->mutex);
558
559 if ( _session->last_msg ) {
560 _session->last_msg->next = _msg;
561 _session->last_msg = _msg;
562 } else {
563 _session->last_msg = _session->oldest_msg = _msg;
564 }
565
566 pthread_mutex_unlock(&_session->mutex);
567
568 return 0;
569}
570
571
572
573/**
574 * @brief Stores headers and payload data in one container ( data )
575 * and the length is set accordingly. Returned message is used for sending _only_.
576 *
577 * @param session The control session.
578 * @param data Payload data to send ( This is what you pass ).
579 * @param length Size of the payload data.
580 * @return RTPMessage* Created message.
581 * @retval NULL Error occurred.
582 */
583RTPMessage* rtp_new_message ( RTPSession* session, const uint8_t* data, uint32_t length )
584{
585 if ( !session )
586 return NULL;
587
588 uint8_t* _from_pos;
589 RTPMessage* _retu = calloc(1, sizeof (RTPMessage));
590 assert(_retu);
591
592 /* Sets header values and copies the extension header in _retu */
593 _retu->header = build_header ( session ); /* It allocates memory and all */
594 _retu->ext_header = session->ext_header;
595
596
597 uint32_t _total_length = length + _retu->header->length;
598
599 if ( _retu->ext_header ) {
600 _total_length += ( 4 /* Minimum ext header len */ + _retu->ext_header->length * size_32 );
601
602 _from_pos = add_header ( _retu->header, _retu->data );
603 _from_pos = add_ext_header ( _retu->ext_header, _from_pos + 1 );
604 } else {
605 _from_pos = add_header ( _retu->header, _retu->data );
606 }
607
608 /*
609 * Parses the extension header into the message
610 * Of course if any
611 */
612
613 /* Appends _data on to _retu->_data */
614 memcpy ( _from_pos, data, length );
615
616 _retu->length = _total_length;
617
618 _retu->next = NULL;
619
620 return _retu;
621}
622
623
624
625
626
627
628
629/********************************************************************************************************************
630 ********************************************************************************************************************
631 ********************************************************************************************************************
632 ********************************************************************************************************************
633 ********************************************************************************************************************
634 *
635 *
636 *
637 * PUBLIC API FUNCTIONS IMPLEMENTATIONS
638 *
639 *
640 *
641 ********************************************************************************************************************
642 ********************************************************************************************************************
643 ********************************************************************************************************************
644 ********************************************************************************************************************
645 ********************************************************************************************************************/
646
647
648
649
650
651
652
653
654
655/**
656 * @brief Release all messages held by session.
657 *
658 * @param session The session.
659 * @return int
660 * @retval -1 Error occurred.
661 * @retval 0 Success.
662 */
663int rtp_release_session_recv ( RTPSession* session )
664{
665 if ( !session ){
666 return -1;
667 }
668
669 RTPMessage* _tmp,* _it;
670
671 pthread_mutex_lock(&session->mutex);
672
673 for ( _it = session->oldest_msg; _it; _it = _tmp ){
674 _tmp = _it->next;
675 rtp_free_msg( session, _it);
676 }
677
678 session->last_msg = session->oldest_msg = NULL;
679
680 pthread_mutex_unlock(&session->mutex);
681
682 return 0;
683}
684
685
686/**
687 * @brief Gets oldest message in the list.
688 *
689 * @param session Where the list is.
690 * @return RTPMessage* The message. You _must_ call rtp_msg_free() to free it.
691 * @retval NULL No messages in the list, or no list.
692 */
693RTPMessage* rtp_recv_msg ( RTPSession* session )
694{
695 if ( !session )
696 return NULL;
697
698 RTPMessage* _retu = session->oldest_msg;
699
700 pthread_mutex_lock(&session->mutex);
701
702 if ( _retu )
703 session->oldest_msg = _retu->next;
704
705 if ( !session->oldest_msg )
706 session->last_msg = NULL;
707
708 pthread_mutex_unlock(&session->mutex);
709
710 return _retu;
711}
712
713
714/**
715 * @brief Sends data to _RTPSession::dest
716 *
717 * @param session The session.
718 * @param messenger Tox* object.
719 * @param data The payload.
720 * @param length Size of the payload.
721 * @return int
722 * @retval -1 On error.
723 * @retval 0 On success.
724 */
725int rtp_send_msg ( RTPSession* session, Messenger* messenger, const uint8_t* data, uint16_t length )
726{
727 RTPMessage* msg = rtp_new_message (session, data, length);
728
729 if ( !msg ) return -1;
730
731 uint8_t _send_data [ MAX_UDP_PACKET_SIZE ];
732
733 _send_data[0] = session->prefix;
734
735 /* Generate the right nonce */
736 uint8_t _calculated[crypto_box_NONCEBYTES];
737 memcpy(_calculated, session->encrypt_nonce, crypto_box_NONCEBYTES);
738 increase_nonce ( _calculated, msg->header->sequnum );
739
740 /* Need to skip 2 bytes that are for sequnum */
741 int encrypted_length = encrypt_data_symmetric( /* TODO: msg->length - 2 (fix this properly)*/
742 (uint8_t*) session->encrypt_key, _calculated, msg->data + 2, msg->length, _send_data + 3 );
743
744 int full_length = encrypted_length + 3;
745
746 _send_data[1] = msg->data[0];
747 _send_data[2] = msg->data[1];
748
749
750 /*if ( full_length != sendpacket ( messenger->net, *((IP_Port*) &session->dest), _send_data, full_length) ) {*/
751 if ( full_length != send_custom_user_packet(messenger, session->dest, _send_data, full_length) ) {
752 printf("Rtp error: %s\n", strerror(errno));
753 return -1;
754 }
755
756
757 /* Set sequ number */
758 if ( session->sequnum >= MAX_SEQU_NUM ) {
759 session->sequnum = 0;
760 memcpy(session->encrypt_nonce, _calculated, crypto_box_NONCEBYTES);
761 } else {
762 session->sequnum++;
763 }
764
765 rtp_free_msg ( session, msg );
766 return 0;
767}
768
769
770/**
771 * @brief Speaks for it self.
772 *
773 * @param session The control session msg belongs to. You set it as NULL when freeing recved messages.
774 * Otherwise set it to session the message was created from.
775 * @param msg The message.
776 * @return void
777 */
778void rtp_free_msg ( RTPSession* session, RTPMessage* msg )
779{
780 if ( !session ){
781 free ( msg->header->csrc );
782 if ( msg->ext_header ){
783 free ( msg->ext_header->table );
784 free ( msg->ext_header );
785 }
786 } else {
787 if ( session->csrc != msg->header->csrc )
788 free ( msg->header->csrc );
789 if ( msg->ext_header && session->ext_header != msg->ext_header ) {
790 free ( msg->ext_header->table );
791 free ( msg->ext_header );
792 }
793 }
794
795 free ( msg->header );
796 free ( msg );
797}
798
799
800/**
801 * @brief Must be called before calling any other rtp function. It's used
802 * to initialize RTP control session.
803 *
804 * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType
805 * @param messenger Tox* object.
806 * @param friend_num Friend id.
807 * @param encrypt_key Speaks for it self.
808 * @param decrypt_key Speaks for it self.
809 * @param encrypt_nonce Speaks for it self.
810 * @param decrypt_nonce Speaks for it self.
811 * @return RTPSession* Created control session.
812 * @retval NULL Error occurred.
813 */
814RTPSession* rtp_init_session ( int payload_type,
815 Messenger* messenger,
816 int friend_num,
817 const uint8_t* encrypt_key,
818 const uint8_t* decrypt_key,
819 const uint8_t* encrypt_nonce,
820 const uint8_t* decrypt_nonce )
821{
822 RTPSession* _retu = calloc(1, sizeof(RTPSession));
823 assert(_retu);
824
825 /*networking_registerhandler(messenger->net, payload_type, rtp_handle_packet, _retu);*/
826 if ( -1 == custom_user_packet_registerhandler(messenger, friend_num, payload_type, rtp_handle_packet, _retu) )
827 {
828 fprintf(stderr, "Error setting custom register handler for rtp session\n");
829 free(_retu);
830 return NULL;
831 }
832
833 _retu->version = RTP_VERSION; /* It's always 2 */
834 _retu->padding = 0; /* If some additional data is needed about the packet */
835 _retu->extension = 0; /* If extension to header is needed */
836 _retu->cc = 1; /* Amount of contributors */
837 _retu->csrc = NULL; /* Container */
838 _retu->ssrc = random_int();
839 _retu->marker = 0;
840 _retu->payload_type = payload_table[payload_type];
841
842 _retu->dest = friend_num;
843
844 _retu->rsequnum = _retu->sequnum = 1;
845
846 _retu->ext_header = NULL; /* When needed allocate */
847 _retu->framerate = -1;
848 _retu->resolution = -1;
849
850 _retu->encrypt_key = encrypt_key;
851 _retu->decrypt_key = decrypt_key;
852
853 /* Need to allocate new memory */
854 _retu->encrypt_nonce = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->encrypt_nonce);
855 _retu->decrypt_nonce = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->decrypt_nonce);
856 _retu->nonce_cycle = calloc ( crypto_box_NONCEBYTES, sizeof (uint8_t) ); assert(_retu->nonce_cycle);
857
858 memcpy(_retu->encrypt_nonce, encrypt_nonce, crypto_box_NONCEBYTES);
859 memcpy(_retu->decrypt_nonce, decrypt_nonce, crypto_box_NONCEBYTES);
860 memcpy(_retu->nonce_cycle , decrypt_nonce, crypto_box_NONCEBYTES);
861
862 _retu->csrc = calloc(1, sizeof (uint32_t));
863 assert(_retu->csrc);
864
865 _retu->csrc[0] = _retu->ssrc; /* Set my ssrc to the list receive */
866
867 /* Also set payload type as prefix */
868 _retu->prefix = payload_type;
869
870 _retu->oldest_msg = _retu->last_msg = NULL;
871
872 pthread_mutex_init(&_retu->mutex, NULL);
873 /*
874 *
875 */
876 return _retu;
877}
878
879
880/**
881 * @brief Terminate the session.
882 *
883 * @param session The session.
884 * @param messenger The messenger who owns the session
885 * @return int
886 * @retval -1 Error occurred.
887 * @retval 0 Success.
888 */
889int rtp_terminate_session ( RTPSession* session, Messenger* messenger )
890{
891 if ( !session )
892 return -1;
893
894 custom_user_packet_registerhandler(messenger, session->dest, session->prefix, NULL, NULL);
895
896 free ( session->ext_header );
897 free ( session->csrc );
898 free ( session->decrypt_nonce );
899 free ( session->encrypt_nonce );
900 free ( session->nonce_cycle );
901
902 pthread_mutex_destroy(&session->mutex);
903
904 /* And finally free session */
905 free ( session );
906
907 return 0;
908} \ No newline at end of file
diff --git a/toxav/rtp.h b/toxav/rtp.h
new file mode 100644
index 00000000..acec8b0a
--- /dev/null
+++ b/toxav/rtp.h
@@ -0,0 +1,219 @@
1/** toxrtp.h
2 *
3 * Copyright (C) 2013 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 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24#ifndef __TOXRTP
25#define __TOXRTP
26
27#define RTP_VERSION 2
28#include <inttypes.h>
29#include <pthread.h>
30
31#include "../toxcore/util.h"
32#include "../toxcore/network.h"
33#include "../toxcore/net_crypto.h"
34#include "../toxcore/Messenger.h"
35
36#define MAX_SEQU_NUM 65535
37#define MAX_RTP_SIZE 65535
38
39/**
40 * @brief Standard rtp header
41 *
42 */
43
44typedef struct _RTPHeader {
45 uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */
46 uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */
47 uint16_t sequnum; /* Sequence Number */
48 uint32_t timestamp; /* Timestamp */
49 uint32_t ssrc; /* SSRC */
50 uint32_t* csrc; /* CSRC's table */
51 uint32_t length; /* Length of the header in payload string. */
52
53} RTPHeader;
54
55
56/**
57 * @brief Standard rtp extension header.
58 *
59 */
60typedef struct _RTPExtHeader {
61 uint16_t type; /* Extension profile */
62 uint16_t length; /* Number of extensions */
63 uint32_t* table; /* Extension's table */
64
65} RTPExtHeader;
66
67
68/**
69 * @brief Standard rtp message.
70 *
71 */
72typedef struct _RTPMessage {
73 RTPHeader* header;
74 RTPExtHeader* ext_header;
75
76 uint8_t data[MAX_RTP_SIZE];
77 uint32_t length;
78 IP_Port from;
79
80 struct _RTPMessage* next;
81} RTPMessage;
82
83
84/**
85 * @brief Our main session descriptor.
86 * It measures the session variables and controls
87 * the entire session. There are functions for manipulating
88 * the session so tend to use those instead of directly modifying
89 * session parameters.
90 *
91 */
92typedef struct _RTPSession {
93 uint8_t version;
94 uint8_t padding;
95 uint8_t extension;
96 uint8_t cc;
97 uint8_t marker;
98 uint8_t payload_type;
99 uint16_t sequnum; /* Set when sending */
100 uint16_t rsequnum; /* Check when recving msg */
101 uint32_t timestamp;
102 uint32_t ssrc;
103 uint32_t* csrc;
104
105 /* If some additional data must be sent via message
106 * apply it here. Only by allocating this member you will be
107 * automatically placing it within a message.
108 */
109 RTPExtHeader* ext_header;
110
111 /* External header identifiers */
112 int resolution;
113 int framerate;
114
115
116 /* Since these are only references of the
117 * call structure don't allocate or free
118 */
119
120 const uint8_t* encrypt_key;
121 const uint8_t* decrypt_key;
122 uint8_t* encrypt_nonce;
123 uint8_t* decrypt_nonce;
124
125 uint8_t* nonce_cycle;
126
127 RTPMessage* oldest_msg;
128 RTPMessage* last_msg; /* tail */
129
130 /* Msg prefix for core to know when recving */
131 uint8_t prefix;
132
133 pthread_mutex_t mutex;
134 int dest;
135
136} RTPSession;
137
138
139/**
140 * @brief Release all messages held by session.
141 *
142 * @param session The session.
143 * @return int
144 * @retval -1 Error occurred.
145 * @retval 0 Success.
146 */
147int rtp_release_session_recv ( RTPSession* session );
148
149
150/**
151 * @brief Get's oldest message in the list.
152 *
153 * @param session Where the list is.
154 * @return RTPMessage* The message. You need to call rtp_msg_free() to free it.
155 * @retval NULL No messages in the list, or no list.
156 */
157RTPMessage* rtp_recv_msg ( RTPSession* session );
158
159
160/**
161 * @brief Sends msg to _RTPSession::dest
162 *
163 * @param session The session.
164 * @param msg The message
165 * @param messenger Tox* object.
166 * @return int
167 * @retval -1 On error.
168 * @retval 0 On success.
169 */
170int rtp_send_msg ( RTPSession* session, Messenger* messenger, const uint8_t* data, uint16_t length );
171
172
173/**
174 * @brief Speaks for it self.
175 *
176 * @param session The control session msg belongs to. It can be NULL.
177 * @param msg The message.
178 * @return void
179 */
180void rtp_free_msg ( RTPSession* session, RTPMessage* msg );
181
182
183/**
184 * @brief Must be called before calling any other rtp function. It's used
185 * to initialize RTP control session.
186 *
187 * @param payload_type Type of payload used to send. You can use values in toxmsi.h::MSICallType
188 * @param messenger Tox* object.
189 * @param friend_num Friend id.
190 * @param encrypt_key Speaks for it self.
191 * @param decrypt_key Speaks for it self.
192 * @param encrypt_nonce Speaks for it self.
193 * @param decrypt_nonce Speaks for it self.
194 * @return RTPSession* Created control session.
195 * @retval NULL Error occurred.
196 */
197RTPSession* rtp_init_session ( int payload_type,
198 Messenger* messenger,
199 int friend_num,
200 const uint8_t* encrypt_key,
201 const uint8_t* decrypt_key,
202 const uint8_t* encrypt_nonce,
203 const uint8_t* decrypt_nonce );
204
205
206/**
207 * @brief Terminate the session.
208 *
209 * @param session The session.
210 * @param messenger The messenger who owns the session
211 * @return int
212 * @retval -1 Error occurred.
213 * @retval 0 Success.
214 */
215int rtp_terminate_session ( RTPSession* session, Messenger* messenger );
216
217
218
219#endif /* __TOXRTP */
diff --git a/toxav/toxav.c b/toxav/toxav.c
new file mode 100644
index 00000000..b40a1c02
--- /dev/null
+++ b/toxav/toxav.c
@@ -0,0 +1,539 @@
1/** toxav.c
2 *
3 * Copyright (C) 2013 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 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif /* HAVE_CONFIG_H */
27
28#include "media.h"
29#include "rtp.h"
30#include "msi.h"
31
32#include <stdlib.h>
33#include <string.h>
34#include <assert.h>
35
36#include "toxav.h"
37
38#define inline__ inline __attribute__((always_inline))
39
40static const uint8_t audio_index = 0, video_index = 1;
41
42
43typedef enum {
44 ts_closing,
45 ts_running,
46 ts_closed
47
48} ThreadState;
49
50typedef struct _ToxAv
51{
52 Messenger* messenger;
53
54 MSISession* msi_session; /** Main msi session */
55
56 RTPSession* rtp_sessions[2]; /* Audio is first and video is second */
57
58 struct jitter_buffer* j_buf;
59 CodecState* cs;
60
61 void* agent_handler;
62} ToxAv;
63
64/**
65 * @brief Start new A/V session. There can only be one session at the time. If you register more
66 * it will result in undefined behaviour.
67 *
68 * @param messenger The messenger handle.
69 * @param useragent The agent handling A/V session (i.e. phone).
70 * @param ua_name Useragent name.
71 * @param video_width Width of video frame.
72 * @param video_height Height of video frame.
73 * @return ToxAv*
74 * @retval NULL On error.
75 */
76ToxAv* toxav_new( Tox* messenger, void* useragent, const char* ua_name , uint16_t video_width, uint16_t video_height)
77{
78 ToxAv* av = calloc ( sizeof(ToxAv), 1);
79 if (av == NULL)
80 return NULL;
81
82 av->messenger = (Messenger *)messenger;
83
84 av->msi_session = msi_init_session(av->messenger, (const unsigned char*) ua_name );
85 av->msi_session->agent_handler = av;
86
87 av->rtp_sessions[0] = av->rtp_sessions [1] = NULL;
88
89 /* NOTE: This should be user defined or? */
90 av->j_buf = create_queue(20);
91
92 av->cs = codec_init_session(AUDIO_BITRATE, AUDIO_FRAME_DURATION, AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, video_width, video_height, VIDEO_BITRATE);
93
94 av->agent_handler = useragent;
95
96 return av;
97}
98
99/**
100 * @brief Remove A/V session.
101 *
102 * @param av Handler.
103 * @return void
104 */
105void toxav_kill ( ToxAv* av )
106{
107 msi_terminate_session(av->msi_session);
108
109 if ( av->rtp_sessions[audio_index] ) {
110 rtp_terminate_session(av->rtp_sessions[audio_index], av->msi_session->messenger_handle);
111 }
112
113 if ( av->rtp_sessions[video_index] ) {
114 rtp_terminate_session(av->rtp_sessions[video_index], av->msi_session->messenger_handle);
115 }
116
117 codec_terminate_session(av->cs);
118
119 free(av);
120}
121
122/**
123 * @brief Register callback for call state.
124 *
125 * @param callback The callback
126 * @param id One of the ToxAvCallbackID values
127 * @return void
128 */
129void toxav_register_callstate_callback ( ToxAVCallback callback, ToxAvCallbackID id )
130{
131 msi_register_callback((MSICallback)callback, (MSICallbackID) id);
132}
133
134/**
135 * @brief Call user. Use its friend_id.
136 *
137 * @param av Handler.
138 * @param user The user.
139 * @param call_type Call type.
140 * @param ringing_seconds Ringing timeout.
141 * @return int
142 * @retval 0 Success.
143 * @retval ToxAvError On error.
144 */
145int toxav_call (ToxAv* av, int user, ToxAvCallType call_type, int ringing_seconds )
146{
147 if ( av->msi_session->call ) {
148 return ErrorAlreadyInCall;
149 }
150
151 return msi_invite(av->msi_session, call_type, ringing_seconds * 1000, user);
152}
153
154/**
155 * @brief Hangup active call.
156 *
157 * @param av Handler.
158 * @return int
159 * @retval 0 Success.
160 * @retval ToxAvError On error.
161 */
162int toxav_hangup ( ToxAv* av )
163{
164 if ( !av->msi_session->call ) {
165 return ErrorNoCall;
166 }
167
168 if ( av->msi_session->call->state != call_active ) {
169 return ErrorInvalidState;
170 }
171
172 return msi_hangup(av->msi_session);
173}
174
175/**
176 * @brief Answer incomming call.
177 *
178 * @param av Handler.
179 * @param call_type Answer with...
180 * @return int
181 * @retval 0 Success.
182 * @retval ToxAvError On error.
183 */
184int toxav_answer ( ToxAv* av, ToxAvCallType call_type )
185{
186 if ( !av->msi_session->call ) {
187 return ErrorNoCall;
188 }
189
190 if ( av->msi_session->call->state != call_starting ) {
191 return ErrorInvalidState;
192 }
193
194 return msi_answer(av->msi_session, call_type);
195}
196
197/**
198 * @brief Reject incomming call.
199 *
200 * @param av Handler.
201 * @param reason Optional reason. Set NULL if none.
202 * @return int
203 * @retval 0 Success.
204 * @retval ToxAvError On error.
205 */
206int toxav_reject ( ToxAv* av, const char* reason )
207{
208 if ( !av->msi_session->call ) {
209 return ErrorNoCall;
210 }
211
212 if ( av->msi_session->call->state != call_starting ) {
213 return ErrorInvalidState;
214 }
215
216 return msi_reject(av->msi_session, (const uint8_t*) reason);
217}
218
219/**
220 * @brief Cancel outgoing request.
221 *
222 * @param av Handler.
223 * @param reason Optional reason.
224 * @return int
225 * @retval 0 Success.
226 * @retval ToxAvError On error.
227 */
228int toxav_cancel ( ToxAv* av, const char* reason )
229{
230 if ( !av->msi_session->call ) {
231 return ErrorNoCall;
232 }
233
234 return msi_cancel(av->msi_session, 0, (const uint8_t*)reason);
235}
236
237/**
238 * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer.
239 *
240 * @param av Handler.
241 * @return int
242 * @retval 0 Success.
243 * @retval ToxAvError On error.
244 */
245int toxav_stop_call ( ToxAv* av )
246{
247 if ( !av->msi_session->call ) {
248 return ErrorNoCall;
249 }
250
251 return msi_stopcall(av->msi_session);
252}
253
254/**
255 * @brief Must be call before any RTP transmission occurs.
256 *
257 * @param av Handler.
258 * @return int
259 * @retval 0 Success.
260 * @retval ToxAvError On error.
261 */
262int toxav_prepare_transmission ( ToxAv* av )
263{
264 assert(av->msi_session);
265 if ( !av->msi_session || !av->msi_session->call ) {
266 return ErrorNoCall;
267 }
268
269 av->rtp_sessions[audio_index] = rtp_init_session(
270 type_audio,
271 av->messenger,
272 av->msi_session->call->peers[0],
273 av->msi_session->call->key_peer,
274 av->msi_session->call->key_local,
275 av->msi_session->call->nonce_peer,
276 av->msi_session->call->nonce_local
277 );
278
279
280 if ( !av->rtp_sessions[audio_index] ) {
281 fprintf(stderr, "Error while starting audio RTP session!\n");
282 return ErrorStartingAudioRtp;
283 }
284
285 av->rtp_sessions[video_index] = rtp_init_session (
286 type_video,
287 av->messenger,
288 av->msi_session->call->peers[0],
289 av->msi_session->call->key_peer,
290 av->msi_session->call->key_local,
291 av->msi_session->call->nonce_peer,
292 av->msi_session->call->nonce_local
293 );
294
295
296 if ( !av->rtp_sessions[video_index] ) {
297 fprintf(stderr, "Error while starting video RTP session!\n");
298 return ErrorStartingVideoRtp;
299 }
300
301 return ErrorNone;
302}
303
304/**
305 * @brief Call this at the end of the transmission.
306 *
307 * @param av Handler.
308 * @return int
309 * @retval 0 Success.
310 * @retval ToxAvError On error.
311 */
312int toxav_kill_transmission ( ToxAv* av )
313{
314 /* Both sessions should be active at any time */
315 if ( !av->rtp_sessions[0] || !av->rtp_sessions[0] )
316 return ErrorNoTransmission;
317
318
319 if ( -1 == rtp_terminate_session(av->rtp_sessions[audio_index], av->messenger) ) {
320 fprintf(stderr, "Error while terminating audio RTP session!\n");
321 return ErrorTerminatingAudioRtp;
322 }
323
324 if ( -1 == rtp_terminate_session(av->rtp_sessions[video_index], av->messenger) ) {
325 fprintf(stderr, "Error while terminating video RTP session!\n");
326 return ErrorTerminatingVideoRtp;
327 }
328
329 return ErrorNone;
330}
331
332
333/**
334 * @brief Send RTP payload.
335 *
336 * @param av Handler.
337 * @param type Type of payload.
338 * @param payload The payload.
339 * @param length Size of it.
340 * @return int
341 * @retval 0 Success.
342 * @retval -1 Failure.
343 */
344inline__ int toxav_send_rtp_payload ( ToxAv* av, ToxAvCallType type, const uint8_t* payload, uint16_t length )
345{
346 if ( av->rtp_sessions[type - TypeAudio] )
347 return rtp_send_msg ( av->rtp_sessions[type - TypeAudio], av->msi_session->messenger_handle, payload, length );
348 else return -1;
349}
350
351/**
352 * @brief Receive RTP payload.
353 *
354 * @param av Handler.
355 * @param type Type of the payload.
356 * @param dest Storage.
357 * @return int
358 * @retval ToxAvError On Error.
359 * @retval >=0 Size of received payload.
360 */
361inline__ int toxav_recv_rtp_payload ( ToxAv* av, ToxAvCallType type, uint8_t* dest )
362{
363 if ( !dest ) return ErrorInternal;
364
365 if ( !av->rtp_sessions[type - TypeAudio] ) return ErrorNoRtpSession;
366
367 RTPMessage* message;
368
369 if ( type == TypeAudio ) {
370
371 do {
372 message = rtp_recv_msg(av->rtp_sessions[audio_index]);
373
374 if (message) {
375 /* push the packet into the queue */
376 queue(av->j_buf, message);
377 }
378 } while(message);
379
380 int success = 0;
381 message = dequeue(av->j_buf, &success);
382
383 if ( success == 2) return ErrorAudioPacketLost;
384 }
385 else {
386 message = rtp_recv_msg(av->rtp_sessions[video_index]);
387 }
388
389 if ( message ) {
390 memcpy(dest, message->data, message->length);
391
392 int length = message->length;
393
394 rtp_free_msg(NULL, message);
395
396 return length;
397 }
398
399 return 0;
400}
401
402/**
403 * @brief Receive decoded video packet.
404 *
405 * @param av Handler.
406 * @param output Storage.
407 * @return int
408 * @retval 0 Success.
409 * @retval ToxAvError On Error.
410 */
411inline__ int toxav_recv_video ( ToxAv* av, vpx_image_t **output)
412{
413 if ( !output ) return ErrorInternal;
414 uint8_t packet [RTP_PAYLOAD_SIZE];
415 int recved_size = 0;
416 do {
417 recved_size = toxav_recv_rtp_payload(av, TypeVideo, packet);
418 if (recved_size > 0) {
419 printf("decode: %s\n", vpx_codec_err_to_string(vpx_codec_decode(&av->cs->v_decoder, packet, recved_size, NULL, 0)));
420 }
421 }while (recved_size > 0);
422 vpx_codec_iter_t iter = NULL;
423 vpx_image_t *img;
424 img = vpx_codec_get_frame(&av->cs->v_decoder, &iter);
425 if (img == NULL)
426 return ErrorInternal;
427
428 *output = img;
429 return 0;
430}
431
432/**
433 * @brief Encode and send video packet.
434 *
435 * @param av Handler.
436 * @param input The packet.
437 * @return int
438 * @retval 0 Success.
439 * @retval ToxAvError On error.
440 */
441inline__ int toxav_send_video ( ToxAv* av, vpx_image_t *input)
442{
443 if (vpx_codec_encode(&av->cs->v_encoder, input, av->cs->frame_counter, 1, 0, MAX_ENCODE_TIME_US) != VPX_CODEC_OK) {
444 printf("could not encode video frame\n");
445 return ErrorInternal;
446 }
447 ++av->cs->frame_counter;
448
449 vpx_codec_iter_t iter = NULL;
450 const vpx_codec_cx_pkt_t *pkt;
451 int sent = 0;
452 while( (pkt = vpx_codec_get_cx_data(&av->cs->v_encoder, &iter)) ) {
453 if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
454 if (toxav_send_rtp_payload(av, TypeVideo, pkt->data.frame.buf, pkt->data.frame.sz) != -1)
455 ++sent;
456 }
457 }
458 if (sent > 0)
459 return 0;
460
461 return ErrorInternal;
462}
463
464/**
465 * @brief Receive decoded audio frame.
466 *
467 * @param av Handler.
468 * @param frame_size ...
469 * @param dest Destination of the packet. Make sure it has enough space for
470 * RTP_PAYLOAD_SIZE bytes.
471 * @return int
472 * @retval >=0 Size of received packet.
473 * @retval ToxAvError On error.
474 */
475inline__ int toxav_recv_audio ( ToxAv* av, int frame_size, int16_t* dest )
476{
477 if ( !dest ) return ErrorInternal;
478 uint8_t packet [RTP_PAYLOAD_SIZE];
479
480 int recved_size = toxav_recv_rtp_payload(av, TypeAudio, packet);
481
482 if ( recved_size == ErrorAudioPacketLost ) {
483 printf("Lost packet\n");
484 return opus_decode(av->cs->audio_decoder, NULL, 0, dest, frame_size, 1);
485 } else if ( recved_size ){
486 return opus_decode(av->cs->audio_decoder, packet, recved_size, dest, frame_size, 0);
487 } else {
488 return 0; /* Nothing received */
489 }
490}
491
492/**
493 * @brief Encode and send audio frame.
494 *
495 * @param av Handler.
496 * @param frame The frame.
497 * @param frame_size It's size.
498 * @return int
499 * @retval 0 Success.
500 * @retval ToxAvError On error.
501 */
502inline__ int toxav_send_audio ( ToxAv* av, const int16_t* frame, int frame_size)
503{
504 uint8_t temp_data[RTP_PAYLOAD_SIZE];
505 int32_t ret = opus_encode(av->cs->audio_encoder, frame, frame_size, temp_data, sizeof(temp_data));
506 if (ret <= 0)
507 return ErrorInternal;
508
509 return toxav_send_rtp_payload(av, TypeAudio, temp_data, ret);
510}
511
512/**
513 * @brief Get peer transmission type. It can either be audio or video.
514 *
515 * @param av Handler.
516 * @param peer The peer
517 * @return int
518 * @retval ToxAvCallType On success.
519 * @retval ToxAvError On error.
520 */
521int toxav_get_peer_transmission_type ( ToxAv* av, int peer )
522{
523 assert(av->msi_session);
524 if ( peer < 0 || !av->msi_session->call || av->msi_session->call->peer_count <= peer )
525 return ErrorInternal;
526
527 return av->msi_session->call->type_peer[peer];
528}
529
530/**
531 * @brief Get reference to an object that is handling av session.
532 *
533 * @param av Handler.
534 * @return void*
535 */
536void* toxav_get_agent_handler ( ToxAv* av )
537{
538 return av->agent_handler;
539}
diff --git a/toxav/toxav.h b/toxav/toxav.h
new file mode 100644
index 00000000..82334273
--- /dev/null
+++ b/toxav/toxav.h
@@ -0,0 +1,300 @@
1/** toxav.h
2 *
3 * Copyright (C) 2013 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 * Report bugs/suggestions at #tox-dev @ freenode.net:6667
22 */
23
24
25#ifndef __TOXAV
26#define __TOXAV
27#include <inttypes.h>
28
29/* vpx_image_t */
30#include <vpx/vpx_image.h>
31
32typedef void* ( *ToxAVCallback ) ( void* arg );
33typedef struct _ToxAv ToxAv;
34
35#ifndef __TOX_DEFINED__
36#define __TOX_DEFINED__
37typedef struct Tox Tox;
38#endif
39
40#define RTP_PAYLOAD_SIZE 65535
41
42/* Default video bitrate in bytes/s */
43#define VIDEO_BITRATE 10*1000*100
44
45/* Default audio bitrate in bits/s */
46#define AUDIO_BITRATE 64000
47
48/* Number of audio channels. */
49#define AUDIO_CHANNELS 1
50
51/* Audio frame duration in miliseconds */
52#define AUDIO_FRAME_DURATION 20
53
54/* Audio sample rate recommended to be 48kHz for Opus */
55#define AUDIO_SAMPLE_RATE 48000
56
57/* The amount of samples in one audio frame */
58#define AUDIO_FRAME_SIZE AUDIO_SAMPLE_RATE*AUDIO_FRAME_DURATION/1000
59
60/* Assume 60 fps*/
61#define MAX_ENCODE_TIME_US ((1000 / 60) * 1000)
62
63
64/**
65 * @brief Callbacks ids that handle the call states.
66 */
67typedef enum {
68 /* Requests */
69 OnInvite,
70 OnStart,
71 OnCancel,
72 OnReject,
73 OnEnd,
74
75 /* Responses */
76 OnRinging,
77 OnStarting,
78 OnEnding,
79
80 /* Protocol */
81 OnError,
82 OnRequestTimeout
83
84} ToxAvCallbackID;
85
86
87/**
88 * @brief Call type identifier.
89 */
90typedef enum {
91 TypeAudio = 70,
92 TypeVideo
93} ToxAvCallType;
94
95
96/**
97 * @brief Error indicators.
98 *
99 */
100typedef enum {
101 ErrorNone = 0,
102 ErrorInternal = -1, /* Internal error */
103 ErrorAlreadyInCall = -2, /* Already has an active call */
104 ErrorNoCall = -3, /* Trying to perform call action while not in a call */
105 ErrorInvalidState = -4, /* Trying to perform call action while in invalid state*/
106 ErrorNoRtpSession = -5, /* Trying to perform rtp action on invalid session */
107 ErrorAudioPacketLost = -6, /* Indicating packet loss */
108 ErrorStartingAudioRtp = -7, /* Error in toxav_prepare_transmission() */
109 ErrorStartingVideoRtp = -8 , /* Error in toxav_prepare_transmission() */
110 ErrorNoTransmission = -9, /* Returned in toxav_kill_transmission() */
111 ErrorTerminatingAudioRtp = -10, /* Returned in toxav_kill_transmission() */
112 ErrorTerminatingVideoRtp = -11, /* Returned in toxav_kill_transmission() */
113
114} ToxAvError;
115
116
117/**
118 * @brief Start new A/V session. There can only be one session at the time. If you register more
119 * it will result in undefined behaviour.
120 *
121 * @param messenger The messenger handle.
122 * @param useragent The agent handling A/V session (i.e. phone).
123 * @param ua_name Useragent name.
124 * @param video_width Width of video frame.
125 * @param video_height Height of video frame.
126 * @return ToxAv*
127 * @retval NULL On error.
128 */
129ToxAv* toxav_new(Tox* messenger, void* useragent, const char* ua_name, uint16_t video_width, uint16_t video_height);
130
131/**
132 * @brief Remove A/V session.
133 *
134 * @param av Handler.
135 * @return void
136 */
137void toxav_kill(ToxAv* av);
138
139/**
140 * @brief Register callback for call state.
141 *
142 * @param callback The callback
143 * @param id One of the ToxAvCallbackID values
144 * @return void
145 */
146void toxav_register_callstate_callback (ToxAVCallback callback, ToxAvCallbackID id);
147
148/**
149 * @brief Call user. Use its friend_id.
150 *
151 * @param av Handler.
152 * @param user The user.
153 * @param call_type Call type.
154 * @param ringing_seconds Ringing timeout.
155 * @return int
156 * @retval 0 Success.
157 * @retval ToxAvError On error.
158 */
159int toxav_call(ToxAv* av, int user, ToxAvCallType call_type, int ringing_seconds);
160
161/**
162 * @brief Hangup active call.
163 *
164 * @param av Handler.
165 * @return int
166 * @retval 0 Success.
167 * @retval ToxAvError On error.
168 */
169int toxav_hangup(ToxAv* av);
170
171/**
172 * @brief Answer incomming call.
173 *
174 * @param av Handler.
175 * @param call_type Answer with...
176 * @return int
177 * @retval 0 Success.
178 * @retval ToxAvError On error.
179 */
180int toxav_answer(ToxAv* av, ToxAvCallType call_type );
181
182/**
183 * @brief Reject incomming call.
184 *
185 * @param av Handler.
186 * @param reason Optional reason. Set NULL if none.
187 * @return int
188 * @retval 0 Success.
189 * @retval ToxAvError On error.
190 */
191int toxav_reject(ToxAv* av, const char* reason);
192
193/**
194 * @brief Cancel outgoing request.
195 *
196 * @param av Handler.
197 * @param reason Optional reason.
198 * @return int
199 * @retval 0 Success.
200 * @retval ToxAvError On error.
201 */
202int toxav_cancel(ToxAv* av, const char* reason);
203
204/**
205 * @brief Terminate transmission. Note that transmission will be terminated without informing remote peer.
206 *
207 * @param av Handler.
208 * @return int
209 * @retval 0 Success.
210 * @retval ToxAvError On error.
211 */
212int toxav_stop_call(ToxAv* av);
213
214/**
215 * @brief Must be call before any RTP transmission occurs.
216 *
217 * @param av Handler.
218 * @return int
219 * @retval 0 Success.
220 * @retval ToxAvError On error.
221 */
222int toxav_prepare_transmission(ToxAv* av);
223
224/**
225 * @brief Call this at the end of the transmission.
226 *
227 * @param av Handler.
228 * @return int
229 * @retval 0 Success.
230 * @retval ToxAvError On error.
231 */
232int toxav_kill_transmission(ToxAv* av);
233
234/**
235 * @brief Receive decoded video packet.
236 *
237 * @param av Handler.
238 * @param output Storage.
239 * @return int
240 * @retval 0 Success.
241 * @retval ToxAvError On Error.
242 */
243int toxav_recv_video ( ToxAv* av, vpx_image_t **output);
244
245/**
246 * @brief Receive decoded audio frame.
247 *
248 * @param av Handler.
249 * @param frame_size ...
250 * @param dest Destination of the packet. Make sure it has enough space for
251 * RTP_PAYLOAD_SIZE bytes.
252 * @return int
253 * @retval >=0 Size of received packet.
254 * @retval ToxAvError On error.
255 */
256int toxav_recv_audio( ToxAv* av, int frame_size, int16_t* dest );
257
258/**
259 * @brief Encode and send video packet.
260 *
261 * @param av Handler.
262 * @param input The packet.
263 * @return int
264 * @retval 0 Success.
265 * @retval ToxAvError On error.
266 */
267int toxav_send_video ( ToxAv* av, vpx_image_t *input);
268
269/**
270 * @brief Encode and send audio frame.
271 *
272 * @param av Handler.
273 * @param frame The frame.
274 * @param frame_size It's size.
275 * @return int
276 * @retval 0 Success.
277 * @retval ToxAvError On error.
278 */
279int toxav_send_audio ( ToxAv* av, const int16_t* frame, int frame_size);
280
281/**
282 * @brief Get peer transmission type. It can either be audio or video.
283 *
284 * @param av Handler.
285 * @param peer The peer
286 * @return int
287 * @retval ToxAvCallType On success.
288 * @retval ToxAvError On error.
289 */
290int toxav_get_peer_transmission_type ( ToxAv* av, int peer );
291
292/**
293 * @brief Get reference to an object that is handling av session.
294 *
295 * @param av Handler.
296 * @return void*
297 */
298void* toxav_get_agent_handler ( ToxAv* av );
299
300#endif /* __TOXAV */ \ No newline at end of file