diff options
Diffstat (limited to 'toxav')
-rw-r--r-- | toxav/codec.c | 20 | ||||
-rw-r--r-- | toxav/codec.h | 8 | ||||
-rw-r--r-- | toxav/msi.c | 581 | ||||
-rw-r--r-- | toxav/msi.h | 71 | ||||
-rw-r--r-- | toxav/rtp.c | 8 | ||||
-rw-r--r-- | toxav/rtp.h | 55 | ||||
-rw-r--r-- | toxav/toxav.c | 6 | ||||
-rw-r--r-- | toxav/toxav.h | 21 |
8 files changed, 359 insertions, 411 deletions
diff --git a/toxav/codec.c b/toxav/codec.c index e6fe713e..9fc14071 100644 --- a/toxav/codec.c +++ b/toxav/codec.c | |||
@@ -1,8 +1,6 @@ | |||
1 | /** codec.c | 1 | /** codec.c |
2 | * | 2 | * |
3 | * Audio and video codec intitialization, encoding/decoding and playback | 3 | * Copyright (C) 2013-2015 Tox project All Rights Reserved. |
4 | * | ||
5 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
6 | * | 4 | * |
7 | * This file is part of Tox. | 5 | * This file is part of Tox. |
8 | * | 6 | * |
@@ -124,7 +122,7 @@ static void buffer_free(PayloadBuffer *b) | |||
124 | } | 122 | } |
125 | 123 | ||
126 | /* JITTER BUFFER WORK */ | 124 | /* JITTER BUFFER WORK */ |
127 | typedef struct JitterBuffer { | 125 | typedef struct JitterBuffer_s { |
128 | RTPMessage **queue; | 126 | RTPMessage **queue; |
129 | uint32_t size; | 127 | uint32_t size; |
130 | uint32_t capacity; | 128 | uint32_t capacity; |
@@ -711,11 +709,6 @@ int cs_enable_audio_sending(CSSession* cs, uint32_t bitrate, int channels) | |||
711 | if (cs->audio_encoder) | 709 | if (cs->audio_encoder) |
712 | return 0; | 710 | return 0; |
713 | 711 | ||
714 | /** | ||
715 | * Encoder is initialized with default values. These values (Sampling rate, channel count) | ||
716 | * change on the fly from toxav. | ||
717 | */ | ||
718 | |||
719 | int rc = OPUS_OK; | 712 | int rc = OPUS_OK; |
720 | cs->audio_encoder = opus_encoder_create(48000, channels, OPUS_APPLICATION_AUDIO, &rc); | 713 | cs->audio_encoder = opus_encoder_create(48000, channels, OPUS_APPLICATION_AUDIO, &rc); |
721 | 714 | ||
@@ -750,12 +743,7 @@ int cs_enable_audio_receiving(CSSession* cs) | |||
750 | { | 743 | { |
751 | if (cs->audio_decoder) | 744 | if (cs->audio_decoder) |
752 | return 0; | 745 | return 0; |
753 | 746 | ||
754 | /** | ||
755 | * Decoder is initialized with default values. These values (Sampling rate, channel count) | ||
756 | * change on the fly from toxav. | ||
757 | */ | ||
758 | |||
759 | int rc; | 747 | int rc; |
760 | cs->audio_decoder = opus_decoder_create(48000, 2, &rc ); | 748 | cs->audio_decoder = opus_decoder_create(48000, 2, &rc ); |
761 | 749 | ||
@@ -792,7 +780,7 @@ void queue_message(RTPSession *session, RTPMessage *msg) | |||
792 | if (!cs) return; | 780 | if (!cs) return; |
793 | 781 | ||
794 | /* Audio */ | 782 | /* Audio */ |
795 | if (session->payload_type == msi_TypeAudio % 128) { | 783 | if (session->payload_type == rtp_TypeAudio % 128) { |
796 | pthread_mutex_lock(cs->queue_mutex); | 784 | pthread_mutex_lock(cs->queue_mutex); |
797 | int ret = jbuf_write(cs->j_buf, msg); | 785 | int ret = jbuf_write(cs->j_buf, msg); |
798 | pthread_mutex_unlock(cs->queue_mutex); | 786 | pthread_mutex_unlock(cs->queue_mutex); |
diff --git a/toxav/codec.h b/toxav/codec.h index 02a3b1b4..b5eb19e2 100644 --- a/toxav/codec.h +++ b/toxav/codec.h | |||
@@ -1,8 +1,6 @@ | |||
1 | /** codec.h | 1 | /** codec.h |
2 | * | 2 | * |
3 | * Audio and video codec intitialization, encoding/decoding and playback | 3 | * Copyright (C) 2013-2015 Tox project All Rights Reserved. |
4 | * | ||
5 | * Copyright (C) 2013 Tox project All Rights Reserved. | ||
6 | * | 4 | * |
7 | * This file is part of Tox. | 5 | * This file is part of Tox. |
8 | * | 6 | * |
@@ -69,7 +67,7 @@ typedef enum { | |||
69 | /** | 67 | /** |
70 | * Codec session - controling codec | 68 | * Codec session - controling codec |
71 | */ | 69 | */ |
72 | typedef struct _CSSession { | 70 | typedef struct CSSession_s { |
73 | 71 | ||
74 | /* VIDEO | 72 | /* VIDEO |
75 | * | 73 | * |
@@ -117,7 +115,7 @@ typedef struct _CSSession { | |||
117 | int32_t last_pack_channels; | 115 | int32_t last_pack_channels; |
118 | int32_t last_packet_sampling_rate; | 116 | int32_t last_packet_sampling_rate; |
119 | int32_t last_packet_frame_duration; | 117 | int32_t last_packet_frame_duration; |
120 | struct JitterBuffer *j_buf; | 118 | struct JitterBuffer_s *j_buf; |
121 | 119 | ||
122 | 120 | ||
123 | /* Voice activity detection */ | 121 | /* Voice activity detection */ |
diff --git a/toxav/msi.c b/toxav/msi.c index e78c57fe..c1342950 100644 --- a/toxav/msi.c +++ b/toxav/msi.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /** msi.c | 1 | /** msi.c |
2 | * | 2 | * |
3 | * Copyright (C) 2013 Tox project All Rights Reserved. | 3 | * Copyright (C) 2013-2015 Tox project All Rights Reserved. |
4 | * | 4 | * |
5 | * This file is part of Tox. | 5 | * This file is part of Tox. |
6 | * | 6 | * |
@@ -36,11 +36,6 @@ | |||
36 | 36 | ||
37 | #define MSI_MAXMSG_SIZE 256 | 37 | #define MSI_MAXMSG_SIZE 256 |
38 | 38 | ||
39 | /* Define default timeout for a request. | ||
40 | * There is no behavior specified by the msi on what will | ||
41 | * client do on timeout, but to call timeout callback. | ||
42 | */ | ||
43 | #define m_deftout 10000 /* in milliseconds */ | ||
44 | 39 | ||
45 | /** | 40 | /** |
46 | * Protocol: | 41 | * Protocol: |
@@ -53,17 +48,11 @@ typedef enum { | |||
53 | IDResponse, | 48 | IDResponse, |
54 | IDError, | 49 | IDError, |
55 | IDCapabilities, | 50 | IDCapabilities, |
51 | IDMVFSZ, | ||
52 | IDMVFPSZ, | ||
56 | 53 | ||
57 | } MSIHeaderID; | 54 | } MSIHeaderID; |
58 | 55 | ||
59 | /** | ||
60 | * Headers | ||
61 | */ | ||
62 | typedef enum { | ||
63 | type_request, | ||
64 | type_response, | ||
65 | } MSIMessageType; | ||
66 | |||
67 | typedef enum { | 56 | typedef enum { |
68 | requ_invite, | 57 | requ_invite, |
69 | requ_start, | 58 | requ_start, |
@@ -77,17 +66,20 @@ typedef enum { | |||
77 | resp_error, | 66 | resp_error, |
78 | } MSIResponse; | 67 | } MSIResponse; |
79 | 68 | ||
69 | |||
80 | #define GENERIC_HEADER(header, val_type) \ | 70 | #define GENERIC_HEADER(header, val_type) \ |
81 | typedef struct { \ | 71 | typedef struct { \ |
82 | val_type value; \ | 72 | val_type value; \ |
83 | _Bool exists; \ | 73 | bool exists; \ |
84 | } MSIHeader##header; | 74 | } MSIHeader##header |
85 | 75 | ||
86 | 76 | ||
87 | GENERIC_HEADER ( Request, MSIRequest ) | 77 | GENERIC_HEADER ( Request, MSIRequest ); |
88 | GENERIC_HEADER ( Response, MSIResponse ) | 78 | GENERIC_HEADER ( Response, MSIResponse ); |
89 | GENERIC_HEADER ( Error, MSIError ) | 79 | GENERIC_HEADER ( Error, MSIError ); |
90 | GENERIC_HEADER ( Capabilities, uint8_t ) | 80 | GENERIC_HEADER ( Capabilities, uint8_t ); |
81 | GENERIC_HEADER ( MVFSZ, uint16_t ); | ||
82 | GENERIC_HEADER ( MVFPSZ, uint16_t ); | ||
91 | 83 | ||
92 | 84 | ||
93 | typedef struct { | 85 | typedef struct { |
@@ -95,63 +87,36 @@ typedef struct { | |||
95 | MSIHeaderResponse response; | 87 | MSIHeaderResponse response; |
96 | MSIHeaderError error; | 88 | MSIHeaderError error; |
97 | MSIHeaderCapabilities capabilities; | 89 | MSIHeaderCapabilities capabilities; |
90 | MSIHeaderMVFSZ mvfsz; /* Max video frame size. NOTE: Value must be in network b-order */ | ||
91 | MSIHeaderMVFPSZ mvfpsz; /* Max video frame piece size. NOTE: Value must be in network b-order */ | ||
98 | } MSIMessage; | 92 | } MSIMessage; |
99 | 93 | ||
100 | 94 | ||
101 | static void invoke_callback(MSICall* c, MSICallbackID i) | 95 | static int parse_input ( MSIMessage *dest, const uint8_t *data, uint16_t length ) |
102 | { | ||
103 | if ( c->session->callbacks[i] ) { | ||
104 | LOGGER_DEBUG("Invoking callback function: %d", i); | ||
105 | c->session->callbacks[i] ( c->session->agent_handler, c ); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * Create the message. | ||
111 | */ | ||
112 | static MSIMessage *msi_new_message ( MSIMessageType type, const uint8_t type_value ) | ||
113 | { | ||
114 | MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); | ||
115 | |||
116 | if ( retu == NULL ) { | ||
117 | LOGGER_WARNING("Allocation failed! Program might misbehave!"); | ||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | if ( type == type_request ) { | ||
122 | retu->request.exists = 1; | ||
123 | retu->request.value = type_value; | ||
124 | |||
125 | } else { | ||
126 | retu->response.exists = 1; | ||
127 | retu->response.value = type_value; | ||
128 | } | ||
129 | |||
130 | return retu; | ||
131 | } | ||
132 | |||
133 | |||
134 | /** | ||
135 | * Parse raw data received from socket into MSIMessage struct. | ||
136 | */ | ||
137 | static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t length ) | ||
138 | { | 96 | { |
139 | 97 | /* Parse raw data received from socket into MSIMessage struct */ | |
98 | |||
140 | #define CHECK_SIZE(bytes, constraint, size) \ | 99 | #define CHECK_SIZE(bytes, constraint, size) \ |
141 | if ((constraint -= 3) < 1) { LOGGER_ERROR("Read over length!"); return -1; } \ | 100 | if ((constraint -= (2 + size)) < 1) { LOGGER_ERROR("Read over length!"); return -1; } \ |
142 | if ( bytes[1] != size ) { LOGGER_ERROR("Invalid data size!"); return -1; } | 101 | if ( bytes[1] != size ) { LOGGER_ERROR("Invalid data size!"); return -1; } |
143 | 102 | ||
144 | #define CHECK_ENUM_HIGH(bytes, enum_high) \ | 103 | #define CHECK_ENUM_HIGH(bytes, enum_high) /* Assumes size == 1 */ \ |
145 | if ( bytes[2] > enum_high ) { LOGGER_ERROR("Failed enum high limit!"); return -1; } | 104 | if ( bytes[2] > enum_high ) { LOGGER_ERROR("Failed enum high limit!"); return -1; } |
146 | 105 | ||
147 | #define SET_VALUES(bytes, header) do { \ | 106 | #define SET_UINT8(bytes, header) do { \ |
148 | header.value = bytes[2]; \ | 107 | header.value = bytes[2]; \ |
149 | header.exists = 1; \ | 108 | header.exists = true; \ |
150 | bytes += 3; \ | 109 | bytes += 3; \ |
151 | } while(0) | 110 | } while(0) |
152 | 111 | ||
153 | 112 | #define SET_UINT16(bytes, header) do { \ | |
154 | if ( msg == NULL ) { | 113 | memcpy(&header.value, bytes + 2, 2);\ |
114 | header.exists = true; \ | ||
115 | bytes += 4; \ | ||
116 | } while(0) | ||
117 | |||
118 | |||
119 | if ( dest == NULL ) { | ||
155 | LOGGER_ERROR("Could not parse message: no storage!"); | 120 | LOGGER_ERROR("Could not parse message: no storage!"); |
156 | return -1; | 121 | return -1; |
157 | } | 122 | } |
@@ -169,25 +134,35 @@ static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t lengt | |||
169 | case IDRequest: | 134 | case IDRequest: |
170 | CHECK_SIZE(it, size_constraint, 1); | 135 | CHECK_SIZE(it, size_constraint, 1); |
171 | CHECK_ENUM_HIGH(it, requ_end); | 136 | CHECK_ENUM_HIGH(it, requ_end); |
172 | SET_VALUES(it, msg->request); | 137 | SET_UINT8(it, dest->request); |
173 | break; | 138 | break; |
174 | 139 | ||
175 | case IDResponse: | 140 | case IDResponse: |
176 | CHECK_SIZE(it, size_constraint, 1); | 141 | CHECK_SIZE(it, size_constraint, 1); |
177 | CHECK_ENUM_HIGH(it, resp_error); | 142 | CHECK_ENUM_HIGH(it, resp_error); |
178 | SET_VALUES(it, msg->response); | 143 | SET_UINT8(it, dest->response); |
179 | it += 3; | 144 | it += 3; |
180 | break; | 145 | break; |
181 | 146 | ||
182 | case IDError: | 147 | case IDError: |
183 | CHECK_SIZE(it, size_constraint, 1); | 148 | CHECK_SIZE(it, size_constraint, 1); |
184 | CHECK_ENUM_HIGH(it, msi_ErrUndisclosed); | 149 | CHECK_ENUM_HIGH(it, msi_ErrUndisclosed); |
185 | SET_VALUES(it, msg->error); | 150 | SET_UINT8(it, dest->error); |
186 | break; | 151 | break; |
187 | 152 | ||
188 | case IDCapabilities: | 153 | case IDCapabilities: |
189 | CHECK_SIZE(it, size_constraint, 1); | 154 | CHECK_SIZE(it, size_constraint, 1); |
190 | SET_VALUES(it, msg->capabilities); | 155 | SET_UINT8(it, dest->capabilities); |
156 | break; | ||
157 | |||
158 | case IDMVFSZ: | ||
159 | CHECK_SIZE(it, size_constraint, 2); | ||
160 | SET_UINT16(it, dest->mvfsz); | ||
161 | break; | ||
162 | |||
163 | case IDMVFPSZ: | ||
164 | CHECK_SIZE(it, size_constraint, 2); | ||
165 | SET_UINT16(it, dest->mvfpsz); | ||
191 | break; | 166 | break; |
192 | 167 | ||
193 | default: | 168 | default: |
@@ -201,42 +176,15 @@ static int parse_raw_data ( MSIMessage *msg, const uint8_t *data, uint16_t lengt | |||
201 | 176 | ||
202 | #undef CHECK_SIZE | 177 | #undef CHECK_SIZE |
203 | #undef CHECK_ENUM_HIGH | 178 | #undef CHECK_ENUM_HIGH |
204 | #undef SET_VALUES | 179 | #undef SET_UINT8 |
205 | } | 180 | #undef SET_UINT16 |
206 | |||
207 | /** | ||
208 | * Parse data from handle_packet. | ||
209 | */ | ||
210 | static MSIMessage *parse_in ( const uint8_t *data, uint16_t length ) | ||
211 | { | ||
212 | if ( data == NULL ) { | ||
213 | LOGGER_WARNING("Tried to parse empty message!"); | ||
214 | return NULL; | ||
215 | } | ||
216 | |||
217 | MSIMessage *retu = calloc ( sizeof ( MSIMessage ), 1 ); | ||
218 | |||
219 | if ( retu == NULL ) { | ||
220 | LOGGER_WARNING("Allocation failed! Program might misbehave!"); | ||
221 | return NULL; | ||
222 | } | ||
223 | |||
224 | if ( parse_raw_data ( retu, data, length ) == -1 ) { | ||
225 | |||
226 | free ( retu ); | ||
227 | return NULL; | ||
228 | } | ||
229 | |||
230 | return retu; | ||
231 | } | 181 | } |
232 | 182 | ||
233 | 183 | static uint8_t *parse_header ( MSIHeaderID id, uint8_t *dest, const void *value, | |
234 | /** | 184 | uint8_t value_len, uint16_t *length ) |
235 | * Speaks for itself. | ||
236 | */ | ||
237 | static uint8_t *prepare_header ( MSIHeaderID id, uint8_t *dest, const void *value, | ||
238 | uint8_t value_len, uint16_t *length ) | ||
239 | { | 185 | { |
186 | /* Parse a single header for sending */ | ||
187 | |||
240 | if ( dest == NULL ) { | 188 | if ( dest == NULL ) { |
241 | LOGGER_ERROR("No destination space!"); | 189 | LOGGER_ERROR("No destination space!"); |
242 | return NULL; | 190 | return NULL; |
@@ -260,60 +208,65 @@ static uint8_t *prepare_header ( MSIHeaderID id, uint8_t *dest, const void *valu | |||
260 | } | 208 | } |
261 | 209 | ||
262 | 210 | ||
263 | /** | 211 | |
264 | * Parse MSIMessage to send. Returns size in bytes of the parsed message | 212 | static void call_invoke_callback(MSICall* call, MSICallbackID cb) |
265 | */ | ||
266 | static uint16_t parse_out ( MSIMessage *msg, uint8_t *dest ) | ||
267 | { | 213 | { |
268 | if (msg == NULL) { | 214 | if ( call->session->callbacks[cb] ) { |
269 | LOGGER_ERROR("No message!"); | 215 | LOGGER_DEBUG("Invoking callback function: %d", cb); |
270 | return 0; | 216 | call->session->callbacks[cb] ( call->session->agent_handler, call ); |
271 | } | 217 | } |
218 | } | ||
272 | 219 | ||
273 | if (dest == NULL ) { | 220 | static int call_send_message ( MSICall *call, const MSIMessage *msg ) |
274 | LOGGER_ERROR("No destination!"); | 221 | { |
275 | return 0; | 222 | /* Parse and send message */ |
276 | } | 223 | |
224 | uint8_t parsed [MSI_MAXMSG_SIZE]; | ||
277 | 225 | ||
278 | uint8_t *it = dest; | 226 | uint8_t *it = parsed; |
279 | uint16_t size = 0; | 227 | uint16_t size = 0; |
280 | 228 | ||
281 | if (msg->request.exists) { | 229 | if (msg->request.exists) { |
282 | uint8_t cast = msg->request.value; | 230 | uint8_t cast = msg->request.value; |
283 | it = prepare_header(IDRequest, it, &cast, 1, &size); | 231 | it = parse_header(IDRequest, it, &cast, |
232 | sizeof(cast), &size); | ||
284 | } | 233 | } |
285 | 234 | ||
286 | if (msg->response.exists) { | 235 | if (msg->response.exists) { |
287 | uint8_t cast = msg->response.value; | 236 | uint8_t cast = msg->response.value; |
288 | it = prepare_header(IDResponse, it, &cast, 1, &size); | 237 | it = parse_header(IDResponse, it, &cast, |
238 | sizeof(cast), &size); | ||
289 | } | 239 | } |
290 | 240 | ||
291 | if (msg->error.exists) { | 241 | if (msg->error.exists) { |
292 | it = prepare_header(IDError, it, &msg->error.value, sizeof(msg->error.value), &size); | 242 | it = parse_header(IDError, it, &msg->error.value, |
243 | sizeof(msg->error.value), &size); | ||
293 | } | 244 | } |
294 | 245 | ||
295 | if (msg->capabilities.exists) { | 246 | if (msg->capabilities.exists) { |
296 | it = prepare_header(IDCapabilities, it, &msg->capabilities.value, | 247 | it = parse_header(IDCapabilities, it, &msg->capabilities.value, |
297 | sizeof(msg->capabilities.value), &size); | 248 | sizeof(msg->capabilities.value), &size); |
298 | } | 249 | } |
299 | 250 | ||
251 | if (msg->mvfsz.exists) { | ||
252 | it = parse_header(IDMVFSZ, it, &msg->mvfsz.value, | ||
253 | sizeof(msg->mvfsz.value), &size); | ||
254 | } | ||
255 | |||
256 | if (msg->mvfpsz.exists) { | ||
257 | it = parse_header(IDMVFPSZ, it, &msg->mvfpsz.value, | ||
258 | sizeof(msg->mvfpsz.value), &size); | ||
259 | } | ||
260 | |||
300 | *it = 0; | 261 | *it = 0; |
301 | size ++; | 262 | size ++; |
302 | 263 | ||
303 | return size; | 264 | if ( it == parsed ) { |
304 | } | 265 | LOGGER_WARNING("Parsing message failed; empty message"); |
305 | |||
306 | static int send_message ( MSICall *call, MSIMessage *msg, uint32_t to ) | ||
307 | { | ||
308 | uint8_t parsed [MSI_MAXMSG_SIZE]; | ||
309 | uint16_t length = parse_out ( msg, parsed ); | ||
310 | |||
311 | if ( !length ) { | ||
312 | LOGGER_WARNING("Parsing message failed; nothing sent!"); | ||
313 | return -1; | 266 | return -1; |
314 | } | 267 | } |
315 | 268 | ||
316 | if ( m_msi_packet(call->session->messenger_handle, to, parsed, length) ) { | 269 | if ( m_msi_packet(call->session->messenger_handle, call->friend_id, parsed, size) ) { |
317 | LOGGER_DEBUG("Sent message"); | 270 | LOGGER_DEBUG("Sent message"); |
318 | return 0; | 271 | return 0; |
319 | } | 272 | } |
@@ -321,34 +274,25 @@ static int send_message ( MSICall *call, MSIMessage *msg, uint32_t to ) | |||
321 | return -1; | 274 | return -1; |
322 | } | 275 | } |
323 | 276 | ||
324 | static int send_reponse ( MSICall *call, MSIResponse response, uint32_t to ) | 277 | static int call_send_error ( MSICall *call, MSIError error ) |
325 | { | ||
326 | MSIMessage *msg = msi_new_message ( type_response, response ); | ||
327 | int ret = send_message ( call, msg, to ); | ||
328 | free ( msg ); | ||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | static int send_error ( MSICall *call, MSIError error, uint32_t to ) | ||
333 | { | 278 | { |
279 | /* Send error message */ | ||
280 | |||
334 | if (!call) { | 281 | if (!call) { |
335 | LOGGER_WARNING("Cannot handle error on 'null' call"); | 282 | LOGGER_WARNING("Cannot handle error on 'null' call"); |
336 | return -1; | 283 | return -1; |
337 | } | 284 | } |
338 | 285 | ||
339 | LOGGER_DEBUG("Sending error: %d on call: %d", error, call->call_idx); | 286 | LOGGER_DEBUG("Sending error: %d to friend: %d", error, call->friend_id); |
340 | 287 | ||
341 | MSIMessage *msg_error = msi_new_message ( type_response, resp_error ); | 288 | MSIMessage msg_error; |
289 | msg_error.response.exists = true; | ||
290 | msg_error.response.value = resp_error; | ||
342 | 291 | ||
343 | if (!msg_error) | 292 | msg_error.error.exists = true; |
344 | return -1; | 293 | msg_error.error.value = error; |
345 | |||
346 | msg_error->error.exists = 1; | ||
347 | msg_error->error.value = error; | ||
348 | 294 | ||
349 | send_message ( call, msg_error, to ); | 295 | call_send_message ( call, &msg_error ); |
350 | free ( msg_error ); | ||
351 | |||
352 | return 0; | 296 | return 0; |
353 | } | 297 | } |
354 | 298 | ||
@@ -415,7 +359,6 @@ static void kill_call ( MSICall *call ) | |||
415 | if ( call == NULL ) | 359 | if ( call == NULL ) |
416 | return; | 360 | return; |
417 | 361 | ||
418 | |||
419 | MSISession* session = call->session; | 362 | MSISession* session = call->session; |
420 | 363 | ||
421 | MSICall* prev = call->prev; | 364 | MSICall* prev = call->prev; |
@@ -446,7 +389,7 @@ CLEAR: | |||
446 | 389 | ||
447 | 390 | ||
448 | 391 | ||
449 | static void handle_remote_connection_change(Messenger *messenger, int friend_id, uint8_t status, void *session_p) | 392 | static void on_remote_connection_change(Messenger *messenger, int friend_id, uint8_t status, void *session_p) |
450 | { | 393 | { |
451 | (void)messenger; | 394 | (void)messenger; |
452 | MSISession *session = session_p; | 395 | MSISession *session = session_p; |
@@ -458,7 +401,7 @@ static void handle_remote_connection_change(Messenger *messenger, int friend_id, | |||
458 | if (call == NULL) | 401 | if (call == NULL) |
459 | return; | 402 | return; |
460 | 403 | ||
461 | invoke_callback(call, msi_OnPeerTimeout); | 404 | call_invoke_callback(call, msi_OnPeerTimeout); |
462 | kill_call(call); | 405 | kill_call(call); |
463 | } | 406 | } |
464 | break; | 407 | break; |
@@ -471,7 +414,7 @@ static void handle_remote_connection_change(Messenger *messenger, int friend_id, | |||
471 | 414 | ||
472 | 415 | ||
473 | /********** Request handlers **********/ | 416 | /********** Request handlers **********/ |
474 | static int handle_recv_invite ( MSICall *call, MSIMessage *msg ) | 417 | static int handle_recv_invite ( MSICall *call, const MSIMessage *msg ) |
475 | { | 418 | { |
476 | if ( call == NULL ) { | 419 | if ( call == NULL ) { |
477 | LOGGER_WARNING("Session: %p Handling 'invite' on no call"); | 420 | LOGGER_WARNING("Session: %p Handling 'invite' on no call"); |
@@ -482,6 +425,26 @@ static int handle_recv_invite ( MSICall *call, MSIMessage *msg ) | |||
482 | 425 | ||
483 | LOGGER_DEBUG("Session: %p Handling 'invite' friend: %d", call->session, call->friend_id); | 426 | LOGGER_DEBUG("Session: %p Handling 'invite' friend: %d", call->session, call->friend_id); |
484 | 427 | ||
428 | if (!msg->capabilities.exists) { | ||
429 | LOGGER_WARNING("Session: %p Invalid capabilities on 'invite'"); | ||
430 | /* TODO send error */ | ||
431 | return -1; | ||
432 | } | ||
433 | |||
434 | if (!msg->mvfsz.exists) { | ||
435 | LOGGER_WARNING("Session: %p Invalid mvfsz on 'invite'"); | ||
436 | /* TODO send error */ | ||
437 | return -1; | ||
438 | } | ||
439 | |||
440 | if (!msg->mvfpsz.exists) { | ||
441 | LOGGER_WARNING("Session: %p Invalid mvfpsz on 'invite'"); | ||
442 | /* TODO send error */ | ||
443 | return -1; | ||
444 | } | ||
445 | |||
446 | MSIMessage response; | ||
447 | response.response.exists = true; | ||
485 | 448 | ||
486 | if ( call->state == msi_CallRequesting ) { | 449 | if ( call->state == msi_CallRequesting ) { |
487 | /* The rare glare case. | 450 | /* The rare glare case. |
@@ -493,29 +456,54 @@ static int handle_recv_invite ( MSICall *call, MSIMessage *msg ) | |||
493 | 456 | ||
494 | LOGGER_DEBUG("Glare detected!"); | 457 | LOGGER_DEBUG("Glare detected!"); |
495 | 458 | ||
496 | MSIMessage *msg_starting = msi_new_message ( type_response, resp_starting ); | 459 | call->peer_capabilities = msg->capabilities; |
497 | 460 | ||
498 | call->capabilities &= msg->capabilities; | 461 | call->peer_mvfsz = ntohs(msg->mvfsz.value); |
462 | call->peer_mvfpsz = ntohs(msg->mvfpsz.value); | ||
499 | 463 | ||
500 | msg_starting->capabilities.value = call->capabilities; | 464 | /* Send response */ |
501 | msg_starting->capabilities.exists = 1; | 465 | response.response.value = resp_starting; |
466 | call_send_message ( call, &response ); | ||
467 | |||
468 | return 0; | ||
469 | } else if ( call->state == msi_CallActive ) { | ||
470 | /* Changing capabilities. | ||
471 | * We send starting but no response is expected. | ||
472 | * WARNING: if start is sent call is terminated with an error | ||
473 | */ | ||
474 | LOGGER_DEBUG("Peer is changing capabilities"); | ||
475 | |||
476 | call->peer_capabilities = msg->capabilities; | ||
477 | |||
478 | call->peer_mvfsz = ntohs(msg->mvfsz.value); | ||
479 | call->peer_mvfpsz = ntohs(msg->mvfpsz.value); | ||
480 | |||
481 | /* Send response */ | ||
482 | response.response.value = resp_starting; | ||
483 | call_send_message ( call, &response ); | ||
502 | 484 | ||
503 | send_message ( call, msg_starting, call->friend_id ); | ||
504 | free ( msg_starting ); | ||
505 | 485 | ||
486 | call_invoke_callback(call, msi_OnCapabilities); | ||
506 | return 0; | 487 | return 0; |
507 | } | 488 | } |
508 | 489 | ||
509 | call->capabilities = msg->capabilities; | 490 | call->peer_capabilities = msg->capabilities; |
491 | |||
492 | call->peer_mvfsz = ntohs(msg->mvfsz.value); | ||
493 | call->peer_mvfpsz = ntohs(msg->mvfpsz.value); | ||
494 | |||
510 | call->state = msi_CallRequested; | 495 | call->state = msi_CallRequested; |
511 | 496 | ||
512 | send_reponse(call, resp_ringing, call->friend_id); | 497 | /* Send response */ |
513 | invoke_callback(call, msi_OnInvite); | 498 | response.response.value = resp_ringing; |
514 | 499 | call_send_message ( call, &response ); | |
500 | |||
501 | |||
502 | call_invoke_callback(call, msi_OnInvite); | ||
515 | return 0; | 503 | return 0; |
516 | } | 504 | } |
517 | 505 | ||
518 | static int handle_recv_start ( MSICall *call, MSIMessage *msg ) | 506 | static int handle_recv_start ( MSICall *call, const MSIMessage *msg ) |
519 | { | 507 | { |
520 | if ( call == NULL ) { | 508 | if ( call == NULL ) { |
521 | LOGGER_WARNING("Session: %p Handling 'start' on no call"); | 509 | LOGGER_WARNING("Session: %p Handling 'start' on no call"); |
@@ -533,12 +521,12 @@ static int handle_recv_start ( MSICall *call, MSIMessage *msg ) | |||
533 | LOGGER_DEBUG("Session: %p Handling 'start', friend id: %d", call->session, call->friend_id ); | 521 | LOGGER_DEBUG("Session: %p Handling 'start', friend id: %d", call->session, call->friend_id ); |
534 | 522 | ||
535 | call->state = msi_CallActive; | 523 | call->state = msi_CallActive; |
536 | invoke_callback(call, msi_OnStart); | 524 | call_invoke_callback(call, msi_OnStart); |
537 | 525 | ||
538 | return 0; | 526 | return 0; |
539 | } | 527 | } |
540 | 528 | ||
541 | static int handle_recv_reject ( MSICall *call, MSIMessage *msg ) | 529 | static int handle_recv_reject ( MSICall *call, const MSIMessage *msg ) |
542 | { | 530 | { |
543 | if ( call == NULL ) { | 531 | if ( call == NULL ) { |
544 | LOGGER_WARNING("Session: %p Handling 'start' on no call"); | 532 | LOGGER_WARNING("Session: %p Handling 'start' on no call"); |
@@ -555,13 +543,13 @@ static int handle_recv_reject ( MSICall *call, MSIMessage *msg ) | |||
555 | 543 | ||
556 | LOGGER_DEBUG("Session: %p Handling 'reject', friend id: %u", call->session, call->friend_id); | 544 | LOGGER_DEBUG("Session: %p Handling 'reject', friend id: %u", call->session, call->friend_id); |
557 | 545 | ||
558 | invoke_callback(call, msi_OnReject); | 546 | call_invoke_callback(call, msi_OnReject); |
559 | kill_call(call); | 547 | kill_call(call); |
560 | 548 | ||
561 | return 0; | 549 | return 0; |
562 | } | 550 | } |
563 | 551 | ||
564 | static int handle_recv_end ( MSICall *call, MSIMessage *msg ) | 552 | static int handle_recv_end ( MSICall *call, const MSIMessage *msg ) |
565 | { | 553 | { |
566 | (void)msg; | 554 | (void)msg; |
567 | 555 | ||
@@ -572,14 +560,14 @@ static int handle_recv_end ( MSICall *call, MSIMessage *msg ) | |||
572 | 560 | ||
573 | LOGGER_DEBUG("Session: %p Handling 'end', friend id: %d", call->session, call->friend_id); | 561 | LOGGER_DEBUG("Session: %p Handling 'end', friend id: %d", call->session, call->friend_id); |
574 | 562 | ||
575 | invoke_callback(call, msi_OnEnd); | 563 | call_invoke_callback(call, msi_OnEnd); |
576 | kill_call ( call ); | 564 | kill_call ( call ); |
577 | 565 | ||
578 | return 0; | 566 | return 0; |
579 | } | 567 | } |
580 | 568 | ||
581 | /********** Response handlers **********/ | 569 | /********** Response handlers **********/ |
582 | static int handle_recv_ringing ( MSICall *call, MSIMessage *msg ) | 570 | static int handle_recv_ringing ( MSICall *call, const MSIMessage *msg ) |
583 | { | 571 | { |
584 | if ( call == NULL ) { | 572 | if ( call == NULL ) { |
585 | LOGGER_WARNING("Session: %p Handling 'start' on no call"); | 573 | LOGGER_WARNING("Session: %p Handling 'start' on no call"); |
@@ -596,36 +584,65 @@ static int handle_recv_ringing ( MSICall *call, MSIMessage *msg ) | |||
596 | 584 | ||
597 | LOGGER_DEBUG("Session: %p Handling 'ringing' friend id: %d", call->session, call->friend_id ); | 585 | LOGGER_DEBUG("Session: %p Handling 'ringing' friend id: %d", call->session, call->friend_id ); |
598 | 586 | ||
599 | invoke_callback(call, msi_OnRinging); | 587 | call_invoke_callback(call, msi_OnRinging); |
600 | return 0; | 588 | return 0; |
601 | } | 589 | } |
602 | static int handle_recv_starting ( MSICall *call, MSIMessage *msg ) | 590 | |
591 | static int handle_recv_starting ( MSICall *call, const MSIMessage *msg ) | ||
603 | { | 592 | { |
604 | if ( call == NULL ) { | 593 | if ( call == NULL ) { |
605 | LOGGER_WARNING("Session: %p Handling 'starting' on non-existing call"); | 594 | LOGGER_WARNING("Session: %p Handling 'starting' on non-existing call"); |
606 | return 0; | 595 | return 0; |
607 | } | 596 | } |
608 | 597 | ||
609 | if ( call->state != msi_CallRequested || call->state != msi_CallRequesting ) { | 598 | if ( call->state == msi_CallActive ) { |
599 | LOGGER_DEBUG("Capabilities change confirmed"); | ||
600 | return 0; | ||
601 | } else if ( call->state != msi_CallRequested || call->state != msi_CallRequesting ) { | ||
610 | LOGGER_WARNING("Session: %p Invalid call state on 'starting'"); | 602 | LOGGER_WARNING("Session: %p Invalid call state on 'starting'"); |
611 | /* TODO send error */ | 603 | /* TODO send error */ |
612 | return -1; | 604 | return -1; |
613 | } | 605 | } |
614 | 606 | ||
615 | MSIMessage *msg_start = msi_new_message ( type_request, requ_start ); | ||
616 | send_message ( call, msg_start, call->friend_id ); | ||
617 | free ( msg_start ); | ||
618 | |||
619 | if (call->state == msi_CallRequesting) { | 607 | if (call->state == msi_CallRequesting) { |
608 | if (!msg->capabilities.exists) { | ||
609 | LOGGER_WARNING("Session: %p Invalid capabilities on 'starting'"); | ||
610 | /* TODO send error */ | ||
611 | return -1; | ||
612 | } | ||
613 | |||
614 | if (!msg->mvfsz.exists) { | ||
615 | LOGGER_WARNING("Session: %p Invalid mvfsz on 'invite'"); | ||
616 | /* TODO send error */ | ||
617 | return -1; | ||
618 | } | ||
619 | |||
620 | if (!msg->mvfpsz.exists) { | ||
621 | LOGGER_WARNING("Session: %p Invalid mvfpsz on 'invite'"); | ||
622 | /* TODO send error */ | ||
623 | return -1; | ||
624 | } | ||
625 | |||
626 | call->peer_capabilities = msg->capabilities.value; | ||
627 | |||
628 | call->peer_mvfsz = ntohs(msg->mvfsz.value); | ||
629 | call->peer_mvfpsz = ntohs(msg->mvfpsz.value); | ||
630 | |||
620 | call->state = msi_CallActive; | 631 | call->state = msi_CallActive; |
621 | invoke_callback(call, msi_OnStart); | 632 | call_invoke_callback(call, msi_OnStart); |
622 | } | 633 | } |
623 | |||
624 | /* Otherwise it's a glare case so don't start until 'start' is recved */ | 634 | /* Otherwise it's a glare case so don't start until 'start' is recved */ |
625 | 635 | ||
636 | /* Send start in either case (glare or normal) */ | ||
637 | MSIMessage msg_start; | ||
638 | msg_start.request.exists = true; | ||
639 | msg_start.request.value = requ_start; | ||
640 | call_send_message ( call, &msg_start ); | ||
641 | |||
626 | return 0; | 642 | return 0; |
627 | } | 643 | } |
628 | static int handle_recv_error ( MSICall *call, MSIMessage *msg ) | 644 | |
645 | static int handle_recv_error ( MSICall *call, const MSIMessage *msg ) | ||
629 | { | 646 | { |
630 | if ( call == NULL ) { | 647 | if ( call == NULL ) { |
631 | LOGGER_WARNING("Handling 'error' on non-existing call!"); | 648 | LOGGER_WARNING("Handling 'error' on non-existing call!"); |
@@ -634,46 +651,15 @@ static int handle_recv_error ( MSICall *call, MSIMessage *msg ) | |||
634 | 651 | ||
635 | LOGGER_DEBUG("Session: %p Handling 'error' friend id: %d", call->session, call->friend_id ); | 652 | LOGGER_DEBUG("Session: %p Handling 'error' friend id: %d", call->session, call->friend_id ); |
636 | 653 | ||
637 | invoke_callback(call, msi_OnError); | 654 | call_invoke_callback(call, msi_OnError); |
638 | 655 | ||
639 | /* TODO Handle error accordingly */ | 656 | /* TODO Handle error accordingly */ |
640 | 657 | ||
641 | return -1; | 658 | return -1; |
642 | } | 659 | } |
643 | 660 | ||
644 | /** | 661 | static void handle_msi_packet ( Messenger *messenger, int friend_id, const uint8_t *data, |
645 | * BASIC call flow: | 662 | uint16_t length, void *object ) |
646 | * | ||
647 | * ALICE BOB | ||
648 | * | invite --> | | ||
649 | * | | | ||
650 | * | <-- ringing | | ||
651 | * | | | ||
652 | * | <-- starting | | ||
653 | * | | | ||
654 | * | start --> | | ||
655 | * | | | ||
656 | * | <-- MEDIA TRANS --> | | ||
657 | * | | | ||
658 | * | end --> | | ||
659 | * | | | ||
660 | * | <-- ending | | ||
661 | * | ||
662 | * Alice calls Bob by sending invite packet. | ||
663 | * Bob recvs the packet and sends an ringing packet; | ||
664 | * which notifies Alice that her invite is acknowledged. | ||
665 | * Ringing screen shown on both sides. | ||
666 | * Bob accepts the invite for a call by sending starting packet. | ||
667 | * Alice recvs the starting packet and sends the started packet to | ||
668 | * inform Bob that she recved the starting packet. | ||
669 | * Now the media transmission is established ( i.e. RTP transmission ). | ||
670 | * Alice hangs up and sends end packet. | ||
671 | * Bob recves the end packet and sends ending packet | ||
672 | * as the acknowledgement that the call is ending. | ||
673 | * | ||
674 | * | ||
675 | */ | ||
676 | static void msi_handle_packet ( Messenger *messenger, int friend_id, const uint8_t *data, uint16_t length, void *object ) | ||
677 | { | 663 | { |
678 | LOGGER_DEBUG("Got msi message"); | 664 | LOGGER_DEBUG("Got msi message"); |
679 | 665 | ||
@@ -681,11 +667,9 @@ static void msi_handle_packet ( Messenger *messenger, int friend_id, const uint8 | |||
681 | (void)messenger; | 667 | (void)messenger; |
682 | 668 | ||
683 | MSISession *session = object; | 669 | MSISession *session = object; |
684 | MSIMessage *msg; | 670 | MSIMessage msg; |
685 | 671 | ||
686 | msg = parse_in ( data, length ); | 672 | if ( parse_input ( &msg, data, length ) == -1 ) { |
687 | |||
688 | if ( !msg ) { | ||
689 | LOGGER_WARNING("Error parsing message"); | 673 | LOGGER_WARNING("Error parsing message"); |
690 | return; | 674 | return; |
691 | } else { | 675 | } else { |
@@ -697,7 +681,7 @@ static void msi_handle_packet ( Messenger *messenger, int friend_id, const uint8 | |||
697 | MSICall *call = get_call(session, friend_id); | 681 | MSICall *call = get_call(session, friend_id); |
698 | 682 | ||
699 | if (call == NULL) { | 683 | if (call == NULL) { |
700 | if (msg->request != requ_invite) { | 684 | if (msg.request != requ_invite) { |
701 | /* TODO send error */ | 685 | /* TODO send error */ |
702 | return; | 686 | return; |
703 | } | 687 | } |
@@ -712,36 +696,36 @@ static void msi_handle_packet ( Messenger *messenger, int friend_id, const uint8 | |||
712 | 696 | ||
713 | /* Now handle message */ | 697 | /* Now handle message */ |
714 | int rc = 0; | 698 | int rc = 0; |
715 | if ( msg->request.exists ) { /* Handle request */ | 699 | if ( msg.request.exists ) { /* Handle request */ |
716 | switch (msg->request.value) { | 700 | switch (msg.request.value) { |
717 | case requ_invite: | 701 | case requ_invite: |
718 | rc = handle_recv_invite ( call, msg ); | 702 | rc = handle_recv_invite ( call, &msg ); |
719 | break; | 703 | break; |
720 | 704 | ||
721 | case requ_start: | 705 | case requ_start: |
722 | rc = handle_recv_start ( call, msg ); | 706 | rc = handle_recv_start ( call, &msg ); |
723 | break; | 707 | break; |
724 | 708 | ||
725 | case requ_reject: | 709 | case requ_reject: |
726 | rc = handle_recv_reject ( call, msg ); | 710 | rc = handle_recv_reject ( call, &msg ); |
727 | break; | 711 | break; |
728 | 712 | ||
729 | case requ_end: | 713 | case requ_end: |
730 | rc = handle_recv_end ( call, msg ); | 714 | rc = handle_recv_end ( call, &msg ); |
731 | break; | 715 | break; |
732 | } | 716 | } |
733 | } else if ( msg->response.exists ) { /* Handle response */ | 717 | } else if ( msg.response.exists ) { /* Handle response */ |
734 | switch (msg->response.value) { | 718 | switch (msg.response.value) { |
735 | case resp_ringing: | 719 | case resp_ringing: |
736 | rc = handle_recv_ringing ( call, msg ); | 720 | rc = handle_recv_ringing ( call, &msg ); |
737 | break; | 721 | break; |
738 | 722 | ||
739 | case resp_starting: | 723 | case resp_starting: |
740 | rc = handle_recv_starting ( call, msg ); | 724 | rc = handle_recv_starting ( call, &msg ); |
741 | break; | 725 | break; |
742 | 726 | ||
743 | case resp_error: | 727 | case resp_error: |
744 | rc = handle_recv_error ( call, msg ); | 728 | rc = handle_recv_error ( call, &msg ); |
745 | break; | 729 | break; |
746 | } | 730 | } |
747 | } else { | 731 | } else { |
@@ -753,7 +737,6 @@ static void msi_handle_packet ( Messenger *messenger, int friend_id, const uint8 | |||
753 | if (rc == -1) | 737 | if (rc == -1) |
754 | kill_call(call); | 738 | kill_call(call); |
755 | 739 | ||
756 | free ( msg ); | ||
757 | pthread_mutex_unlock(session->mutex); | 740 | pthread_mutex_unlock(session->mutex); |
758 | } | 741 | } |
759 | 742 | ||
@@ -787,10 +770,10 @@ MSISession *msi_new ( Messenger *messenger ) | |||
787 | 770 | ||
788 | retu->messenger_handle = messenger; | 771 | retu->messenger_handle = messenger; |
789 | 772 | ||
790 | m_callback_msi_packet(messenger, msi_handle_packet, retu ); | 773 | m_callback_msi_packet(messenger, handle_msi_packet, retu ); |
791 | 774 | ||
792 | /* This is called when remote terminates session */ | 775 | /* This is called when remote terminates session */ |
793 | m_callback_connectionstatus_internal_av(messenger, handle_remote_connection_change, retu); | 776 | m_callback_connectionstatus_internal_av(messenger, on_remote_connection_change, retu); |
794 | 777 | ||
795 | LOGGER_DEBUG("New msi session: %p ", retu); | 778 | LOGGER_DEBUG("New msi session: %p ", retu); |
796 | return retu; | 779 | return retu; |
@@ -807,15 +790,15 @@ int msi_kill ( MSISession *session ) | |||
807 | pthread_mutex_lock(session->mutex); | 790 | pthread_mutex_lock(session->mutex); |
808 | 791 | ||
809 | if (session->calls) { | 792 | if (session->calls) { |
810 | MSIMessage *msg_end = msi_new_message ( type_request, requ_end ); | 793 | MSIMessage msg_end; |
794 | msg_end.request.exists = true; | ||
795 | msg_end.request.value = requ_end; | ||
811 | 796 | ||
812 | MSICall* it = get_call(session, session->calls_head); | 797 | MSICall* it = get_call(session, session->calls_head); |
813 | for (; it; it = it->next) { | 798 | for (; it; it = it->next) { |
814 | send_message(it, msg_end, it->friend_id); | 799 | call_send_message(it, &msg_end); |
815 | kill_call(it); | 800 | kill_call(it); /* This will eventually free session->calls */ |
816 | } | 801 | } |
817 | |||
818 | free(msg_end); | ||
819 | } | 802 | } |
820 | 803 | ||
821 | pthread_mutex_unlock(session->mutex); | 804 | pthread_mutex_unlock(session->mutex); |
@@ -840,15 +823,22 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_ | |||
840 | if ( *call == NULL ) | 823 | if ( *call == NULL ) |
841 | return -1; | 824 | return -1; |
842 | 825 | ||
843 | *call->capabilities = capabilities; | 826 | *call->self_capabilities = capabilities; |
827 | |||
828 | MSIMessage msg_invite; | ||
829 | msg_invite.request.exists = true; | ||
830 | msg_invite.request.value = requ_invite; | ||
844 | 831 | ||
845 | MSIMessage *msg_invite = msi_new_message ( type_request, requ_invite ); | 832 | msg_invite.capabilities.exists = true; |
833 | msg_invite.capabilities.value = capabilities; | ||
846 | 834 | ||
847 | msg_invite->capabilities.value = capabilities; | 835 | msg_invite.mvfsz.exists = true; |
848 | msg_invite->capabilities.exists = 1; | 836 | msg_invite.mvfsz.value = htons(D_MVFSZ); |
849 | 837 | ||
850 | send_message ( *call, msg_invite, friend_id ); | 838 | msg_invite.mvfpsz.exists = true; |
851 | free( msg_invite ); | 839 | msg_invite.mvfpsz.value = htons(D_MVFPSZ); |
840 | |||
841 | call_send_message ( *call, &msg_invite ); | ||
852 | 842 | ||
853 | *call->state = msi_CallRequesting; | 843 | *call->state = msi_CallRequesting; |
854 | 844 | ||
@@ -860,9 +850,10 @@ int msi_hangup ( MSICall* call ) | |||
860 | { | 850 | { |
861 | LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index); | 851 | LOGGER_DEBUG("Session: %p Hanging up call: %u", session, call_index); |
862 | 852 | ||
863 | MSIMessage *msg_end = msi_new_message ( type_request, requ_end ); | 853 | MSIMessage msg_end; |
864 | send_message ( call, msg_end, call->friend_id ); | 854 | msg_end.request.exists = true; |
865 | free ( msg_end ); | 855 | msg_end.request.value = requ_end; |
856 | call_send_message ( call, &msg_end ); | ||
866 | 857 | ||
867 | kill_call(call); | 858 | kill_call(call); |
868 | return 0; | 859 | return 0; |
@@ -870,22 +861,29 @@ int msi_hangup ( MSICall* call ) | |||
870 | 861 | ||
871 | int msi_answer ( MSICall* call, uint8_t capabilities ) | 862 | int msi_answer ( MSICall* call, uint8_t capabilities ) |
872 | { | 863 | { |
873 | LOGGER_DEBUG("Session: %p Answering call: %u", session, call_index); | 864 | LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_id); |
874 | 865 | ||
875 | if ( call->state != msi_CallRequested ) { | 866 | if ( call->state != msi_CallRequested ) { |
876 | LOGGER_ERROR("Call is in invalid state!"); | 867 | LOGGER_ERROR("Call is in invalid state!"); |
877 | return -1; | 868 | return -1; |
878 | } | 869 | } |
879 | 870 | ||
880 | call->capabilities = capabilities; | 871 | call->self_capabilities = capabilities; |
872 | |||
873 | MSIMessage msg_starting; | ||
874 | msg_starting.response.exists = true; | ||
875 | msg_starting.response.value = resp_starting; | ||
876 | |||
877 | msg_starting.capabilities.exists = true; | ||
878 | msg_starting.capabilities.value = capabilities; | ||
881 | 879 | ||
882 | MSIMessage *msg_starting = msi_new_message ( type_response, resp_starting ); | 880 | msg_starting.mvfsz.exists = true; |
881 | msg_starting.mvfsz.value = htons(D_MVFSZ); | ||
883 | 882 | ||
884 | msg_starting->capabilities.value = capabilities; | 883 | msg_starting.mvfpsz.exists = true; |
885 | msg_starting->capabilities.exists = 1; | 884 | msg_starting.mvfpsz.value = htons(D_MVFPSZ); |
886 | 885 | ||
887 | send_message ( call, msg_starting, call->friend_id ); | 886 | call_send_message ( call, &msg_starting ); |
888 | free ( msg_starting ); | ||
889 | 887 | ||
890 | return 0; | 888 | return 0; |
891 | } | 889 | } |
@@ -896,57 +894,30 @@ int msi_reject ( MSICall* call ) | |||
896 | 894 | ||
897 | if ( call->state != msi_CallRequested ) { | 895 | if ( call->state != msi_CallRequested ) { |
898 | LOGGER_ERROR("Call is in invalid state!"); | 896 | LOGGER_ERROR("Call is in invalid state!"); |
899 | return msi_ErrorInvalidState; | 897 | return -1; |
900 | } | 898 | } |
901 | 899 | ||
902 | MSIMessage *msg_reject = msi_new_message ( type_request, requ_reject ); | 900 | MSIMessage msg_reject; |
903 | send_message ( call, msg_reject, call->friend_id ); | 901 | msg_reject.request.exists = true; |
904 | free ( msg_reject ); | 902 | msg_reject.request.value = requ_reject; |
903 | |||
904 | call_send_message ( call, &msg_reject ); | ||
905 | 905 | ||
906 | return 0; | 906 | return 0; |
907 | } | 907 | } |
908 | 908 | ||
909 | int msi_change_csettings( MSICall* call, uint8_t capabilities ) | 909 | int msi_change_csettings( MSICall* call, uint8_t capabilities ) |
910 | { | 910 | { |
911 | pthread_mutex_lock(session->mutex); | 911 | call->self_capabilities = capabilities; |
912 | 912 | ||
913 | LOGGER_DEBUG("Changing media on call: %d", call_index); | 913 | MSIMessage msg_invite; |
914 | 914 | msg_invite.request.exists = true; | |
915 | MSICall *call = session->calls[call_index]; | 915 | msg_invite.request.value = requ_invite; |
916 | 916 | ||
917 | if ( call->state != msi_CallActive ) { | 917 | msg_invite.capabilities.exists = true; |
918 | LOGGER_ERROR("Call is not active!"); | 918 | msg_invite.capabilities.value = capabilities; |
919 | pthread_mutex_unlock(session->mutex); | 919 | |
920 | return msi_ErrorInvalidState; | 920 | call_send_message ( *call, &msg_invite ); |
921 | } | 921 | |
922 | |||
923 | MSICSettings *local = &call->csettings_local; | ||
924 | |||
925 | if ( | ||
926 | local->call_type == csettings->call_type && | ||
927 | local->video_bitrate == csettings->video_bitrate && | ||
928 | local->max_video_width == csettings->max_video_width && | ||
929 | local->max_video_height == csettings->max_video_height && | ||
930 | local->audio_bitrate == csettings->audio_bitrate && | ||
931 | local->audio_frame_duration == csettings->audio_frame_duration && | ||
932 | local->audio_sample_rate == csettings->audio_sample_rate && | ||
933 | local->audio_channels == csettings->audio_channels ) { | ||
934 | LOGGER_ERROR("Call is already set accordingly!"); | ||
935 | pthread_mutex_unlock(session->mutex); | ||
936 | return -1; | ||
937 | } | ||
938 | |||
939 | *local = *csettings; | ||
940 | |||
941 | MSIMessage *msg_invite = msi_new_message ( type_request, requ_invite ); | ||
942 | |||
943 | msi_msg_set_csettings ( msg_invite, local ); | ||
944 | send_message ( session, call, msg_invite, call->peers[0] ); | ||
945 | free ( msg_invite ); | ||
946 | |||
947 | LOGGER_DEBUG("Request for media change sent"); | ||
948 | |||
949 | pthread_mutex_unlock(session->mutex); | ||
950 | |||
951 | return 0; | 922 | return 0; |
952 | } \ No newline at end of file | 923 | } \ No newline at end of file |
diff --git a/toxav/msi.h b/toxav/msi.h index 8fa309c3..a1eb499b 100644 --- a/toxav/msi.h +++ b/toxav/msi.h | |||
@@ -1,6 +1,6 @@ | |||
1 | /** msi.h | 1 | /** msi.h |
2 | * | 2 | * |
3 | * Copyright (C) 2013 Tox project All Rights Reserved. | 3 | * Copyright (C) 2013-2015 Tox project All Rights Reserved. |
4 | * | 4 | * |
5 | * This file is part of Tox. | 5 | * This file is part of Tox. |
6 | * | 6 | * |
@@ -28,17 +28,9 @@ | |||
28 | #include "codec.h" | 28 | #include "codec.h" |
29 | #include "../toxcore/Messenger.h" | 29 | #include "../toxcore/Messenger.h" |
30 | 30 | ||
31 | typedef uint8_t MSICallIDType[12]; | 31 | /** Preconfigured values for video splitting */ |
32 | typedef uint8_t MSIReasonStrType[255]; | 32 | #define D_MVFSZ 40000 /* 256KiB */ |
33 | typedef void ( *MSICallbackType ) ( void *agent, int32_t call_idx); | 33 | #define D_MVFPSZ 500 /* 1.25 KiB*/ |
34 | |||
35 | /** | ||
36 | * Call type identifier. Also used as rtp callback prefix. | ||
37 | */ | ||
38 | typedef enum { | ||
39 | msi_TypeAudio = 192, | ||
40 | msi_TypeVideo | ||
41 | } MSICallType; | ||
42 | 34 | ||
43 | /** | 35 | /** |
44 | * Error codes. | 36 | * Error codes. |
@@ -62,31 +54,12 @@ typedef enum { | |||
62 | * Call state identifiers. | 54 | * Call state identifiers. |
63 | */ | 55 | */ |
64 | typedef enum { | 56 | typedef enum { |
57 | msi_CallInactive, /* Default */ | ||
58 | msi_CallActive, | ||
65 | msi_CallRequesting, /* when sending call invite */ | 59 | msi_CallRequesting, /* when sending call invite */ |
66 | msi_CallRequested, /* when getting call invite */ | 60 | msi_CallRequested, /* when getting call invite */ |
67 | msi_CallActive, | ||
68 | msi_CallHold, | ||
69 | msi_CallOver | ||
70 | |||
71 | } MSICallState; | 61 | } MSICallState; |
72 | 62 | ||
73 | |||
74 | /** | ||
75 | * Encoding settings. | ||
76 | */ | ||
77 | typedef struct { | ||
78 | MSICallType call_type; | ||
79 | |||
80 | uint32_t video_bitrate; /* In kbits/s */ | ||
81 | uint16_t max_video_width; /* In px */ | ||
82 | uint16_t max_video_height; /* In px */ | ||
83 | |||
84 | uint32_t audio_bitrate; /* In bits/s */ | ||
85 | uint16_t audio_frame_duration; /* In ms */ | ||
86 | uint32_t audio_sample_rate; /* In Hz */ | ||
87 | uint32_t audio_channels; | ||
88 | } MSICSettings; | ||
89 | |||
90 | /** | 63 | /** |
91 | * Callbacks ids that handle the states | 64 | * Callbacks ids that handle the states |
92 | */ | 65 | */ |
@@ -97,32 +70,25 @@ typedef enum { | |||
97 | msi_OnReject, /* The side that was invited rejected the call */ | 70 | msi_OnReject, /* The side that was invited rejected the call */ |
98 | msi_OnEnd, /* Call that was active ended */ | 71 | msi_OnEnd, /* Call that was active ended */ |
99 | msi_OnError, /* Call that was active ended */ | 72 | msi_OnError, /* Call that was active ended */ |
100 | msi_OnRequestTimeout, /* When the requested action didn't get response in specified time */ | ||
101 | msi_OnPeerTimeout, /* Peer timed out; stop the call */ | 73 | msi_OnPeerTimeout, /* Peer timed out; stop the call */ |
102 | msi_OnPeerCSChange, /* Peer requested Csettings change */ | 74 | msi_OnCapabilities, /* Peer requested capabilities change */ |
103 | msi_OnSelfCSChange /* Csettings change confirmation */ | ||
104 | } MSICallbackID; | 75 | } MSICallbackID; |
105 | 76 | ||
106 | /** | 77 | /** |
107 | * Errors | ||
108 | */ | ||
109 | typedef enum { | ||
110 | msi_ErrorNoCall = -20, /* Trying to perform call action while not in a call */ | ||
111 | msi_ErrorInvalidState = -21, /* Trying to perform call action while in invalid state*/ | ||
112 | msi_ErrorAlreadyInCallWithPeer = -22, /* Trying to call peer when already in a call with peer */ | ||
113 | msi_ErrorReachedCallLimit = -23, /* Cannot handle more calls */ | ||
114 | } MSIError; | ||
115 | |||
116 | /** | ||
117 | * The call struct. Please do not modify outside msi.c | 78 | * The call struct. Please do not modify outside msi.c |
118 | */ | 79 | */ |
119 | typedef struct MSICall_s { | 80 | typedef struct MSICall_s { |
120 | struct MSISession_s *session; /* Session pointer */ | 81 | struct MSISession_s *session; /* Session pointer */ |
121 | 82 | ||
122 | MSICallState state; | 83 | MSICallState state; |
123 | uint8_t capabilities; /* Active capabilities */ | ||
124 | 84 | ||
125 | uint32_t friend_id; /* Index of this call in MSISession */ | 85 | uint8_t peer_capabilities; /* Peer capabilities */ |
86 | uint8_t self_capabilities; /* Self capabilities */ | ||
87 | |||
88 | uint16_t peer_mvfsz; /* Max video frame size */ | ||
89 | uint16_t peer_mvfpsz; /* Max video frame part size */ | ||
90 | |||
91 | uint32_t friend_id; /* Index of this call in MSISession */ | ||
126 | 92 | ||
127 | struct MSICall_s* next; | 93 | struct MSICall_s* next; |
128 | struct MSICall_s* prev; | 94 | struct MSICall_s* prev; |
@@ -130,6 +96,11 @@ typedef struct MSICall_s { | |||
130 | 96 | ||
131 | 97 | ||
132 | /** | 98 | /** |
99 | * Msi callback type. 'agent' is a pointer to ToxAv | ||
100 | */ | ||
101 | typedef void ( *MSICallbackType ) ( void *agent, MSICall* call); | ||
102 | |||
103 | /** | ||
133 | * Control session struct. Please do not modify outside msi.c | 104 | * Control session struct. Please do not modify outside msi.c |
134 | */ | 105 | */ |
135 | typedef struct MSISession_s { | 106 | typedef struct MSISession_s { |
@@ -142,7 +113,7 @@ typedef struct MSISession_s { | |||
142 | Messenger *messenger_handle; | 113 | Messenger *messenger_handle; |
143 | 114 | ||
144 | pthread_mutex_t mutex[1]; | 115 | pthread_mutex_t mutex[1]; |
145 | MSICallbackType callbacks[10]; | 116 | MSICallbackType callbacks[8]; |
146 | } MSISession; | 117 | } MSISession; |
147 | 118 | ||
148 | /** | 119 | /** |
diff --git a/toxav/rtp.c b/toxav/rtp.c index a50dd7ce..396e0202 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /** rtp.c | 1 | /** rtp.c |
2 | * | 2 | * |
3 | * Copyright (C) 2013 Tox project All Rights Reserved. | 3 | * Copyright (C) 2013-2015 Tox project All Rights Reserved. |
4 | * | 4 | * |
5 | * This file is part of Tox. | 5 | * This file is part of Tox. |
6 | * | 6 | * |
@@ -333,8 +333,6 @@ RTPMessage *msg_parse ( const uint8_t *data, int length ) | |||
333 | return NULL; | 333 | return NULL; |
334 | } | 334 | } |
335 | 335 | ||
336 | retu->next = NULL; | ||
337 | |||
338 | return retu; | 336 | return retu; |
339 | } | 337 | } |
340 | 338 | ||
@@ -415,8 +413,6 @@ RTPMessage *rtp_new_message ( RTPSession *session, const uint8_t *data, uint32_t | |||
415 | 413 | ||
416 | retu->length = total_length; | 414 | retu->length = total_length; |
417 | 415 | ||
418 | retu->next = NULL; | ||
419 | |||
420 | return retu; | 416 | return retu; |
421 | } | 417 | } |
422 | 418 | ||
@@ -497,7 +493,7 @@ int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) | |||
497 | if ( -1 == send_custom_lossy_packet(session->m, session->dest, msg->data, msg->length) ) { | 493 | if ( -1 == send_custom_lossy_packet(session->m, session->dest, msg->data, msg->length) ) { |
498 | LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); | 494 | LOGGER_WARNING("Failed to send full packet (len: %d)! std error: %s", length, strerror(errno)); |
499 | rtp_free_msg ( session, msg ); | 495 | rtp_free_msg ( session, msg ); |
500 | return rtp_ErrorSending; | 496 | return -1; |
501 | } | 497 | } |
502 | 498 | ||
503 | 499 | ||
diff --git a/toxav/rtp.h b/toxav/rtp.h index b1888f6c..e3b38a8e 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h | |||
@@ -1,6 +1,6 @@ | |||
1 | /** rtp.h | 1 | /** rtp.h |
2 | * | 2 | * |
3 | * Copyright (C) 2013 Tox project All Rights Reserved. | 3 | * Copyright (C) 2013-2015 Tox project All Rights Reserved. |
4 | * | 4 | * |
5 | * This file is part of Tox. | 5 | * This file is part of Tox. |
6 | * | 6 | * |
@@ -31,13 +31,18 @@ | |||
31 | #define MAX_SEQU_NUM 65535 | 31 | #define MAX_SEQU_NUM 65535 |
32 | #define MAX_RTP_SIZE 65535 | 32 | #define MAX_RTP_SIZE 65535 |
33 | 33 | ||
34 | /** | ||
35 | * Payload type identifier. Also used as rtp callback prefix. | ||
36 | */ | ||
34 | typedef enum { | 37 | typedef enum { |
35 | rtp_ErrorSending = -40 | 38 | rtp_TypeAudio = 192, |
36 | } RTPError; | 39 | rtp_TypeVideo |
40 | } RTPPayloadType; | ||
41 | |||
37 | /** | 42 | /** |
38 | * Standard rtp header | 43 | * Standard rtp header |
39 | */ | 44 | */ |
40 | typedef struct _RTPHeader { | 45 | typedef struct { |
41 | uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */ | 46 | uint8_t flags; /* Version(2),Padding(1), Ext(1), Cc(4) */ |
42 | uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */ | 47 | uint8_t marker_payloadt; /* Marker(1), PlayLoad Type(7) */ |
43 | uint16_t sequnum; /* Sequence Number */ | 48 | uint16_t sequnum; /* Sequence Number */ |
@@ -51,7 +56,7 @@ typedef struct _RTPHeader { | |||
51 | /** | 56 | /** |
52 | * Standard rtp extension header. | 57 | * Standard rtp extension header. |
53 | */ | 58 | */ |
54 | typedef struct _RTPExtHeader { | 59 | typedef struct { |
55 | uint16_t type; /* Extension profile */ | 60 | uint16_t type; /* Extension profile */ |
56 | uint16_t length; /* Number of extensions */ | 61 | uint16_t length; /* Number of extensions */ |
57 | uint32_t *table; /* Extension's table */ | 62 | uint32_t *table; /* Extension's table */ |
@@ -61,45 +66,43 @@ typedef struct _RTPExtHeader { | |||
61 | /** | 66 | /** |
62 | * Standard rtp message. | 67 | * Standard rtp message. |
63 | */ | 68 | */ |
64 | typedef struct _RTPMessage { | 69 | typedef struct { |
65 | RTPHeader *header; | 70 | RTPHeader *header; |
66 | RTPExtHeader *ext_header; | 71 | RTPExtHeader *ext_header; |
67 | 72 | ||
68 | uint8_t data[MAX_RTP_SIZE]; | 73 | uint8_t data[MAX_RTP_SIZE]; |
69 | uint32_t length; | 74 | uint32_t length; |
70 | |||
71 | struct _RTPMessage *next; | ||
72 | } RTPMessage; | 75 | } RTPMessage; |
73 | 76 | ||
74 | /** | 77 | /** |
75 | * RTP control session. | 78 | * RTP control session. |
76 | */ | 79 | */ |
77 | typedef struct _RTPSession { | 80 | typedef struct { |
78 | uint8_t version; | 81 | uint8_t version; |
79 | uint8_t padding; | 82 | uint8_t padding; |
80 | uint8_t extension; | 83 | uint8_t extension; |
81 | uint8_t cc; | 84 | uint8_t cc; |
82 | uint8_t marker; | 85 | uint8_t marker; |
83 | uint8_t payload_type; | 86 | uint8_t payload_type; |
84 | uint16_t sequnum; /* Set when sending */ | 87 | uint16_t sequnum; /* Set when sending */ |
85 | uint16_t rsequnum; /* Check when recving msg */ | 88 | uint16_t rsequnum; /* Check when recving msg */ |
86 | uint32_t timestamp; | 89 | uint32_t timestamp; |
87 | uint32_t ssrc; | 90 | uint32_t ssrc; |
88 | uint32_t *csrc; | 91 | uint32_t *csrc; |
89 | 92 | ||
90 | /* If some additional data must be sent via message | 93 | /* If some additional data must be sent via message |
91 | * apply it here. Only by allocating this member you will be | 94 | * apply it here. Only by allocating this member you will be |
92 | * automatically placing it within a message. | 95 | * automatically placing it within a message. |
93 | */ | 96 | */ |
94 | RTPExtHeader *ext_header; | 97 | RTPExtHeader *ext_header; |
95 | 98 | ||
96 | /* Msg prefix for core to know when recving */ | 99 | /* Msg prefix for core to know when recving */ |
97 | uint8_t prefix; | 100 | uint8_t prefix; |
98 | 101 | ||
99 | int dest; | 102 | int dest; |
100 | 103 | ||
101 | struct _CSSession *cs; | 104 | struct CSSession_s *cs; |
102 | Messenger* m; | 105 | Messenger *m; |
103 | 106 | ||
104 | } RTPSession; | 107 | } RTPSession; |
105 | 108 | ||
@@ -119,7 +122,7 @@ void rtp_kill ( RTPSession* session ); | |||
119 | int rtp_register_for_receiving (RTPSession *session); | 122 | int rtp_register_for_receiving (RTPSession *session); |
120 | 123 | ||
121 | /** | 124 | /** |
122 | * Sends msg to _RTPSession::dest | 125 | * Sends msg to RTPSession::dest |
123 | */ | 126 | */ |
124 | int rtp_send_msg ( RTPSession* session, const uint8_t* data, uint16_t length ); | 127 | int rtp_send_msg ( RTPSession* session, const uint8_t* data, uint16_t length ); |
125 | 128 | ||
diff --git a/toxav/toxav.c b/toxav/toxav.c index 72f99576..864d16cf 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /** toxav.c | 1 | /** toxav.c |
2 | * | 2 | * |
3 | * Copyright (C) 2013 Tox project All Rights Reserved. | 3 | * Copyright (C) 2013-2015 Tox project All Rights Reserved. |
4 | * | 4 | * |
5 | * This file is part of Tox. | 5 | * This file is part of Tox. |
6 | * | 6 | * |
@@ -843,7 +843,7 @@ bool i_toxav_prepare_transmission(ToxAV* av, IToxAVCall* call) | |||
843 | 843 | ||
844 | 844 | ||
845 | if (c_self->audio_bitrate > 0 || c_peer->audio_bitrate > 0) { /* Prepare audio rtp */ | 845 | if (c_self->audio_bitrate > 0 || c_peer->audio_bitrate > 0) { /* Prepare audio rtp */ |
846 | call->rtps[audio_index] = rtp_new(msi_TypeAudio, av->m, av->msi->calls[call->call_idx]->peers[0]); | 846 | call->rtps[audio_index] = rtp_new(rtp_TypeAudio, av->m, av->msi->calls[call->call_idx]->peers[0]); |
847 | 847 | ||
848 | if ( !call->rtps[audio_index] ) { | 848 | if ( !call->rtps[audio_index] ) { |
849 | LOGGER_ERROR("Error while starting audio RTP session!\n"); | 849 | LOGGER_ERROR("Error while starting audio RTP session!\n"); |
@@ -857,7 +857,7 @@ bool i_toxav_prepare_transmission(ToxAV* av, IToxAVCall* call) | |||
857 | } | 857 | } |
858 | 858 | ||
859 | if (c_self->video_bitrate > 0 || c_peer->video_bitrate > 0) { /* Prepare video rtp */ | 859 | if (c_self->video_bitrate > 0 || c_peer->video_bitrate > 0) { /* Prepare video rtp */ |
860 | call->rtps[video_index] = rtp_new(msi_TypeVideo, av->m, av->msi->calls[call->call_idx]->peers[0]); | 860 | call->rtps[video_index] = rtp_new(rtp_TypeVideo, av->m, av->msi->calls[call->call_idx]->peers[0]); |
861 | 861 | ||
862 | if ( !call->rtps[video_index] ) { | 862 | if ( !call->rtps[video_index] ) { |
863 | LOGGER_ERROR("Error while starting video RTP session!\n"); | 863 | LOGGER_ERROR("Error while starting video RTP session!\n"); |
diff --git a/toxav/toxav.h b/toxav/toxav.h index 04ad3cd0..c1c6019c 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h | |||
@@ -1,3 +1,24 @@ | |||
1 | /** toxav.h | ||
2 | * | ||
3 | * Copyright (C) 2013-2015 Tox project All Rights Reserved. | ||
4 | * | ||
5 | * This file is part of Tox. | ||
6 | * | ||
7 | * Tox is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 3 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * Tox is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with Tox. If not, see <http://www.gnu.org/licenses/>. | ||
19 | * | ||
20 | */ | ||
21 | |||
1 | #ifndef TOXAV_H | 22 | #ifndef TOXAV_H |
2 | #define TOXAV_H | 23 | #define TOXAV_H |
3 | 24 | ||