diff options
Diffstat (limited to 'toxav')
-rw-r--r-- | toxav/av_test.c | 20 | ||||
-rw-r--r-- | toxav/msi.c | 486 | ||||
-rw-r--r-- | toxav/msi.h | 17 | ||||
-rw-r--r-- | toxav/rtp.c | 14 | ||||
-rw-r--r-- | toxav/rtp.h | 7 | ||||
-rw-r--r-- | toxav/toxav.c | 432 | ||||
-rw-r--r-- | toxav/toxav.h | 5 |
7 files changed, 445 insertions, 536 deletions
diff --git a/toxav/av_test.c b/toxav/av_test.c index 3270f39c..594e7232 100644 --- a/toxav/av_test.c +++ b/toxav/av_test.c | |||
@@ -36,19 +36,11 @@ void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, TOXAV_CALL_STATE s | |||
36 | { | 36 | { |
37 | printf("Handling CALL STATE callback: "); | 37 | printf("Handling CALL STATE callback: "); |
38 | 38 | ||
39 | if (((CallControl*)user_data)->ringing) | ||
40 | ((CallControl*)user_data)->ringing = false; | ||
41 | |||
42 | if (((CallControl*)user_data)->paused) | 39 | if (((CallControl*)user_data)->paused) |
43 | ((CallControl*)user_data)->paused = false; | 40 | ((CallControl*)user_data)->paused = false; |
44 | 41 | ||
45 | switch (state) | 42 | switch (state) |
46 | { | 43 | { |
47 | case TOXAV_CALL_STATE_RINGING: { | ||
48 | printf("Ringing"); | ||
49 | ((CallControl*)user_data)->ringing = true; | ||
50 | } break; | ||
51 | |||
52 | case TOXAV_CALL_STATE_NOT_SENDING: { | 44 | case TOXAV_CALL_STATE_NOT_SENDING: { |
53 | printf("Not sending"); | 45 | printf("Not sending"); |
54 | ((CallControl*)user_data)->sending = false; | 46 | ((CallControl*)user_data)->sending = false; |
@@ -230,6 +222,7 @@ int main (int argc, char** argv) | |||
230 | exit(1); \ | 222 | exit(1); \ |
231 | } \ | 223 | } \ |
232 | BobCC.incoming = false; \ | 224 | BobCC.incoming = false; \ |
225 | BobCC.sending = true; /* There is no more start callback when answering */\ | ||
233 | } \ | 226 | } \ |
234 | else if (AliceCC.sending && BobCC.sending) { \ | 227 | else if (AliceCC.sending && BobCC.sending) { \ |
235 | /* TODO rtp */ \ | 228 | /* TODO rtp */ \ |
@@ -238,6 +231,7 @@ int main (int argc, char** argv) | |||
238 | \ | 231 | \ |
239 | TOXAV_ERR_CALL_CONTROL rc; \ | 232 | TOXAV_ERR_CALL_CONTROL rc; \ |
240 | toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); \ | 233 | toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); \ |
234 | AliceCC.ended = true; /* There is no more end callback when hanging up */\ | ||
241 | \ | 235 | \ |
242 | if (rc != TOXAV_ERR_CALL_CONTROL_OK) { \ | 236 | if (rc != TOXAV_ERR_CALL_CONTROL_OK) { \ |
243 | printf("toxav_call_control failed: %d\n", rc); \ | 237 | printf("toxav_call_control failed: %d\n", rc); \ |
@@ -252,13 +246,13 @@ int main (int argc, char** argv) | |||
252 | } | 246 | } |
253 | 247 | ||
254 | printf("\nTrying regular call (Audio and Video)...\n"); | 248 | printf("\nTrying regular call (Audio and Video)...\n"); |
255 | REGULAR_CALL_FLOW(48, 4000); | 249 | // REGULAR_CALL_FLOW(48, 4000); |
256 | 250 | ||
257 | printf("\nTrying regular call (Audio only)...\n"); | 251 | printf("\nTrying regular call (Audio only)...\n"); |
258 | REGULAR_CALL_FLOW(48, 0); | 252 | // REGULAR_CALL_FLOW(48, 0); |
259 | 253 | ||
260 | printf("\nTrying regular call (Video only)...\n"); | 254 | printf("\nTrying regular call (Video only)...\n"); |
261 | REGULAR_CALL_FLOW(0, 4000); | 255 | // REGULAR_CALL_FLOW(0, 4000); |
262 | 256 | ||
263 | #undef REGULAR_CALL_FLOW | 257 | #undef REGULAR_CALL_FLOW |
264 | 258 | ||
@@ -292,7 +286,7 @@ int main (int argc, char** argv) | |||
292 | } | 286 | } |
293 | } | 287 | } |
294 | 288 | ||
295 | while (!AliceCC.ended || !BobCC.ended) | 289 | while (!AliceCC.ended) |
296 | iterate(Bsn, AliceAV, BobAV); | 290 | iterate(Bsn, AliceAV, BobAV); |
297 | 291 | ||
298 | printf("Success!\n"); | 292 | printf("Success!\n"); |
diff --git a/toxav/msi.c b/toxav/msi.c index 16476364..f179a7ab 100644 --- a/toxav/msi.c +++ b/toxav/msi.c | |||
@@ -37,8 +37,6 @@ | |||
37 | 37 | ||
38 | #define MSI_MAXMSG_SIZE 256 | 38 | #define MSI_MAXMSG_SIZE 256 |
39 | 39 | ||
40 | /* TODO send error on any calloc or etc */ | ||
41 | |||
42 | /** | 40 | /** |
43 | * Protocol: | 41 | * Protocol: |
44 | * | 42 | * |
@@ -47,24 +45,17 @@ | |||
47 | 45 | ||
48 | typedef enum { | 46 | typedef enum { |
49 | IDRequest = 1, | 47 | IDRequest = 1, |
50 | IDResponse, | ||
51 | IDError, | 48 | IDError, |
52 | IDCapabilities, | 49 | IDCapabilities, |
53 | IDVFPSZ, | 50 | IDVFPSZ, |
54 | 51 | ||
55 | } MSIHeaderID; | 52 | } MSIHeaderID; |
56 | 53 | ||
57 | typedef enum { | ||
58 | requ_invite, | ||
59 | requ_start, | ||
60 | requ_reject, | ||
61 | requ_end, | ||
62 | } MSIRequest; | ||
63 | 54 | ||
64 | typedef enum { | 55 | typedef enum { |
65 | resp_ringing, | 56 | requ_push, |
66 | resp_starting, | 57 | requ_pop, |
67 | } MSIResponse; | 58 | } MSIRequest; |
68 | 59 | ||
69 | 60 | ||
70 | #define GENERIC_HEADER(header, val_type) \ | 61 | #define GENERIC_HEADER(header, val_type) \ |
@@ -75,7 +66,6 @@ typedef struct { \ | |||
75 | 66 | ||
76 | 67 | ||
77 | GENERIC_HEADER ( Request, MSIRequest ); | 68 | GENERIC_HEADER ( Request, MSIRequest ); |
78 | GENERIC_HEADER ( Response, MSIResponse ); | ||
79 | GENERIC_HEADER ( Error, MSIError ); | 69 | GENERIC_HEADER ( Error, MSIError ); |
80 | GENERIC_HEADER ( Capabilities, uint8_t ); | 70 | GENERIC_HEADER ( Capabilities, uint8_t ); |
81 | GENERIC_HEADER ( VFPSZ, uint16_t ); | 71 | GENERIC_HEADER ( VFPSZ, uint16_t ); |
@@ -83,29 +73,24 @@ GENERIC_HEADER ( VFPSZ, uint16_t ); | |||
83 | 73 | ||
84 | typedef struct { | 74 | typedef struct { |
85 | MSIHeaderRequest request; | 75 | MSIHeaderRequest request; |
86 | MSIHeaderResponse response; | ||
87 | MSIHeaderError error; | 76 | MSIHeaderError error; |
88 | MSIHeaderCapabilities capabilities; | 77 | MSIHeaderCapabilities capabilities; |
89 | MSIHeaderVFPSZ vfpsz; /* Video frame piece size. NOTE: Value must be in network b-order */ | 78 | MSIHeaderVFPSZ vfpsz; /* Video frame piece size. NOTE: Value must be in network b-order */ |
90 | } MSIMessage; | 79 | } MSIMessage; |
91 | 80 | ||
92 | 81 | ||
82 | void msg_init (MSIMessage *dest, MSIRequest request); | ||
93 | int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ); | 83 | int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ); |
94 | uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length ); | 84 | uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length ); |
95 | int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg ); | 85 | int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg ); |
96 | int send_error ( Messenger* m, uint32_t friend_id, MSIError error ); | 86 | int send_error ( Messenger* m, uint32_t friend_id, MSIError error ); |
97 | static void invoke_callback(MSICall* call, MSICallbackID cb); | 87 | static int invoke_callback(MSICall* call, MSICallbackID cb); |
98 | static MSICall *get_call ( MSISession *session, uint32_t friend_id ); | 88 | static MSICall *get_call ( MSISession *session, uint32_t friend_id ); |
99 | MSICall *new_call ( MSISession *session, uint32_t friend_id ); | 89 | MSICall *new_call ( MSISession *session, uint32_t friend_id ); |
100 | void kill_call ( MSICall *call ); | 90 | void kill_call ( MSICall *call ); |
101 | void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data); | 91 | void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data); |
102 | int handle_recv_invite ( MSICall *call, const MSIMessage *msg ); | 92 | void handle_push ( MSICall *call, const MSIMessage *msg ); |
103 | int handle_recv_start ( MSICall *call, const MSIMessage *msg ); | 93 | void handle_pop ( MSICall *call, const MSIMessage *msg ); |
104 | int handle_recv_reject ( MSICall *call, const MSIMessage *msg ); | ||
105 | int handle_recv_end ( MSICall *call, const MSIMessage *msg ); | ||
106 | int handle_recv_ringing ( MSICall *call, const MSIMessage *msg ); | ||
107 | int handle_recv_starting ( MSICall *call, const MSIMessage *msg ); | ||
108 | int handle_recv_error ( MSICall *call, const MSIMessage *msg ); | ||
109 | void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object ); | 94 | void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object ); |
110 | 95 | ||
111 | 96 | ||
@@ -157,13 +142,12 @@ int msi_kill ( MSISession *session ) | |||
157 | pthread_mutex_lock(session->mutex); | 142 | pthread_mutex_lock(session->mutex); |
158 | 143 | ||
159 | if (session->calls) { | 144 | if (session->calls) { |
160 | MSIMessage msg_end; | 145 | MSIMessage msg; |
161 | msg_end.request.exists = true; | 146 | msg_init(&msg, requ_pop); |
162 | msg_end.request.value = requ_end; | ||
163 | 147 | ||
164 | MSICall* it = get_call(session, session->calls_head); | 148 | MSICall* it = get_call(session, session->calls_head); |
165 | for (; it; it = it->next) { | 149 | for (; it; it = it->next) { |
166 | send_message(session->messenger, it->friend_id, &msg_end); | 150 | send_message(session->messenger, it->friend_id, &msg); |
167 | kill_call(it); /* This will eventually free session->calls */ | 151 | kill_call(it); /* This will eventually free session->calls */ |
168 | } | 152 | } |
169 | } | 153 | } |
@@ -191,17 +175,16 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_ | |||
191 | 175 | ||
192 | (*call)->self_capabilities = capabilities; | 176 | (*call)->self_capabilities = capabilities; |
193 | 177 | ||
194 | MSIMessage msg_invite; | 178 | MSIMessage msg; |
195 | msg_invite.request.exists = true; | 179 | msg_init(&msg, requ_push); |
196 | msg_invite.request.value = requ_invite; | ||
197 | 180 | ||
198 | msg_invite.capabilities.exists = true; | 181 | msg.capabilities.exists = true; |
199 | msg_invite.capabilities.value = capabilities; | 182 | msg.capabilities.value = capabilities; |
200 | 183 | ||
201 | msg_invite.vfpsz.exists = true; | 184 | msg.vfpsz.exists = true; |
202 | msg_invite.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE); | 185 | msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE); |
203 | 186 | ||
204 | send_message ( (*call)->session->messenger, (*call)->friend_id, &msg_invite ); | 187 | send_message ( (*call)->session->messenger, (*call)->friend_id, &msg ); |
205 | 188 | ||
206 | (*call)->state = msi_CallRequesting; | 189 | (*call)->state = msi_CallRequesting; |
207 | 190 | ||
@@ -212,10 +195,10 @@ int msi_hangup ( MSICall* call ) | |||
212 | { | 195 | { |
213 | LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_id); | 196 | LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_id); |
214 | 197 | ||
215 | MSIMessage msg_end; | 198 | MSIMessage msg; |
216 | msg_end.request.exists = true; | 199 | msg_init(&msg, requ_pop); |
217 | msg_end.request.value = requ_end; | 200 | |
218 | send_message ( call->session->messenger, call->friend_id, &msg_end ); | 201 | send_message ( call->session->messenger, call->friend_id, &msg ); |
219 | 202 | ||
220 | kill_call(call); | 203 | kill_call(call); |
221 | return 0; | 204 | return 0; |
@@ -225,65 +208,68 @@ int msi_answer ( MSICall* call, uint8_t capabilities ) | |||
225 | LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_id); | 208 | LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_id); |
226 | 209 | ||
227 | if ( call->state != msi_CallRequested ) { | 210 | if ( call->state != msi_CallRequested ) { |
211 | /* Though sending in invalid state will not cause anything wierd | ||
212 | * Its better to not do it like a maniac */ | ||
228 | LOGGER_ERROR("Call is in invalid state!"); | 213 | LOGGER_ERROR("Call is in invalid state!"); |
229 | return -1; | 214 | return -1; |
230 | } | 215 | } |
231 | 216 | ||
232 | call->self_capabilities = capabilities; | 217 | call->self_capabilities = capabilities; |
233 | 218 | ||
234 | MSIMessage msg_starting; | 219 | MSIMessage msg; |
235 | msg_starting.response.exists = true; | 220 | msg_init(&msg, requ_push); |
236 | msg_starting.response.value = resp_starting; | ||
237 | 221 | ||
238 | msg_starting.capabilities.exists = true; | 222 | msg.capabilities.exists = true; |
239 | msg_starting.capabilities.value = capabilities; | 223 | msg.capabilities.value = capabilities; |
240 | 224 | ||
241 | msg_starting.vfpsz.exists = true; | 225 | msg.vfpsz.exists = true; |
242 | msg_starting.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE); | 226 | msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE); |
243 | 227 | ||
244 | send_message ( call->session->messenger, call->friend_id, &msg_starting ); | 228 | send_message ( call->session->messenger, call->friend_id, &msg ); |
229 | |||
230 | call->state = msi_CallActive; | ||
245 | 231 | ||
246 | return 0; | 232 | return 0; |
247 | } | 233 | } |
248 | int msi_reject ( MSICall* call ) | 234 | int msi_change_capabilities( MSICall* call, uint8_t capabilities ) |
249 | { | 235 | { |
250 | LOGGER_DEBUG("Session: %p Rejecting call with friend: %u", call->session, call->friend_id); | 236 | LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_id); |
251 | 237 | ||
252 | if ( call->state != msi_CallRequested ) { | 238 | if ( call->state != msi_CallActive ) { |
239 | /* Sending capabilities change can cause error on other side if | ||
240 | * the call is not active since we don't send header 'vfpsz'. | ||
241 | * If we were to send 'vfpsz' while call is active it would be | ||
242 | * ignored. However, if call is not active peer will expect | ||
243 | * the said header on 'push' so that it could handle the call | ||
244 | * like new. TODO: explain this better | ||
245 | */ | ||
253 | LOGGER_ERROR("Call is in invalid state!"); | 246 | LOGGER_ERROR("Call is in invalid state!"); |
254 | return -1; | 247 | return -1; |
255 | } | 248 | } |
256 | 249 | ||
257 | MSIMessage msg_reject; | ||
258 | msg_reject.request.exists = true; | ||
259 | msg_reject.request.value = requ_reject; | ||
260 | |||
261 | send_message ( call->session->messenger, call->friend_id, &msg_reject ); | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | int msi_change_csettings( MSICall* call, uint8_t capabilities ) | ||
266 | { | ||
267 | call->self_capabilities = capabilities; | 250 | call->self_capabilities = capabilities; |
268 | 251 | ||
269 | MSIMessage msg_invite; | 252 | MSIMessage msg; |
270 | msg_invite.request.exists = true; | 253 | msg_init(&msg, requ_push); |
271 | msg_invite.request.value = requ_invite; | ||
272 | 254 | ||
273 | msg_invite.capabilities.exists = true; | 255 | msg.capabilities.exists = true; |
274 | msg_invite.capabilities.value = capabilities; | 256 | msg.capabilities.value = capabilities; |
275 | 257 | ||
276 | send_message ( call->session->messenger, call->friend_id, &msg_invite ); | 258 | send_message ( call->session->messenger, call->friend_id, &msg ); |
277 | 259 | ||
278 | return 0; | 260 | return 0; |
279 | } | 261 | } |
280 | 262 | ||
281 | 263 | ||
282 | |||
283 | /** | 264 | /** |
284 | * Private functions | 265 | * Private functions |
285 | */ | 266 | */ |
286 | 267 | void msg_init(MSIMessage* dest, MSIRequest request) | |
268 | { | ||
269 | memset(dest, 0, sizeof(*dest)); | ||
270 | dest->request.exists = true; | ||
271 | dest->request.value = request; | ||
272 | } | ||
287 | int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) | 273 | int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) |
288 | { | 274 | { |
289 | /* Parse raw data received from socket into MSIMessage struct */ | 275 | /* Parse raw data received from socket into MSIMessage struct */ |
@@ -314,7 +300,9 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) | |||
314 | LOGGER_ERROR("Invalid end byte"); | 300 | LOGGER_ERROR("Invalid end byte"); |
315 | return -1; | 301 | return -1; |
316 | } | 302 | } |
317 | 303 | ||
304 | memset(dest, 0, sizeof(*dest)); | ||
305 | |||
318 | const uint8_t *it = data; | 306 | const uint8_t *it = data; |
319 | int size_constraint = length; | 307 | int size_constraint = length; |
320 | 308 | ||
@@ -322,17 +310,10 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) | |||
322 | switch (*it) { | 310 | switch (*it) { |
323 | case IDRequest: | 311 | case IDRequest: |
324 | CHECK_SIZE(it, size_constraint, 1); | 312 | CHECK_SIZE(it, size_constraint, 1); |
325 | CHECK_ENUM_HIGH(it, requ_end); | 313 | CHECK_ENUM_HIGH(it, requ_pop); |
326 | SET_UINT8(it, dest->request); | 314 | SET_UINT8(it, dest->request); |
327 | break; | 315 | break; |
328 | 316 | ||
329 | case IDResponse: | ||
330 | CHECK_SIZE(it, size_constraint, 1); | ||
331 | CHECK_ENUM_HIGH(it, resp_starting); | ||
332 | SET_UINT8(it, dest->response); | ||
333 | it += 3; | ||
334 | break; | ||
335 | |||
336 | case IDError: | 317 | case IDError: |
337 | CHECK_SIZE(it, size_constraint, 1); | 318 | CHECK_SIZE(it, size_constraint, 1); |
338 | CHECK_ENUM_HIGH(it, msi_ErrUndisclosed); | 319 | CHECK_ENUM_HIGH(it, msi_ErrUndisclosed); |
@@ -356,6 +337,11 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length ) | |||
356 | } | 337 | } |
357 | } | 338 | } |
358 | 339 | ||
340 | if (dest->request.exists == false) { | ||
341 | LOGGER_ERROR("Invalid request field!"); | ||
342 | return -1; | ||
343 | } | ||
344 | |||
359 | return 0; | 345 | return 0; |
360 | 346 | ||
361 | #undef CHECK_SIZE | 347 | #undef CHECK_SIZE |
@@ -395,17 +381,15 @@ int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg ) | |||
395 | uint8_t cast = msg->request.value; | 381 | uint8_t cast = msg->request.value; |
396 | it = msg_parse_header_out(IDRequest, it, &cast, | 382 | it = msg_parse_header_out(IDRequest, it, &cast, |
397 | sizeof(cast), &size); | 383 | sizeof(cast), &size); |
398 | } | 384 | } else { |
399 | 385 | LOGGER_DEBUG("Must have request field"); | |
400 | if (msg->response.exists) { | 386 | return -1; |
401 | uint8_t cast = msg->response.value; | ||
402 | it = msg_parse_header_out(IDResponse, it, &cast, | ||
403 | sizeof(cast), &size); | ||
404 | } | 387 | } |
405 | 388 | ||
406 | if (msg->error.exists) { | 389 | if (msg->error.exists) { |
407 | it = msg_parse_header_out(IDError, it, &msg->error.value, | 390 | uint8_t cast = msg->error.value; |
408 | sizeof(msg->error.value), &size); | 391 | it = msg_parse_header_out(IDError, it, &cast, |
392 | sizeof(cast), &size); | ||
409 | } | 393 | } |
410 | 394 | ||
411 | if (msg->capabilities.exists) { | 395 | if (msg->capabilities.exists) { |
@@ -418,14 +402,14 @@ int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg ) | |||
418 | sizeof(msg->vfpsz.value), &size); | 402 | sizeof(msg->vfpsz.value), &size); |
419 | } | 403 | } |
420 | 404 | ||
421 | *it = 0; | ||
422 | size ++; | ||
423 | |||
424 | if ( it == parsed ) { | 405 | if ( it == parsed ) { |
425 | LOGGER_WARNING("Parsing message failed; empty message"); | 406 | LOGGER_WARNING("Parsing message failed; empty message"); |
426 | return -1; | 407 | return -1; |
427 | } | 408 | } |
428 | 409 | ||
410 | *it = 0; | ||
411 | size ++; | ||
412 | |||
429 | if ( m_msi_packet(m, friend_id, parsed, size) ) { | 413 | if ( m_msi_packet(m, friend_id, parsed, size) ) { |
430 | LOGGER_DEBUG("Sent message"); | 414 | LOGGER_DEBUG("Sent message"); |
431 | return 0; | 415 | return 0; |
@@ -440,22 +424,36 @@ int send_error ( Messenger* m, uint32_t friend_id, MSIError error ) | |||
440 | 424 | ||
441 | LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_id); | 425 | LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_id); |
442 | 426 | ||
443 | MSIMessage msg_error; | 427 | MSIMessage msg; |
428 | msg_init(&msg, requ_pop); | ||
444 | 429 | ||
445 | msg_error.error.exists = true; | 430 | msg.error.exists = true; |
446 | msg_error.error.value = error; | 431 | msg.error.value = error; |
447 | 432 | ||
448 | send_message ( m, friend_id, &msg_error ); | 433 | send_message ( m, friend_id, &msg ); |
449 | return 0; | 434 | return 0; |
450 | } | 435 | } |
451 | static void invoke_callback(MSICall* call, MSICallbackID cb) | 436 | int invoke_callback(MSICall* call, MSICallbackID cb) |
452 | { | 437 | { |
453 | assert(call); | 438 | assert(call); |
454 | 439 | ||
455 | if ( call->session->callbacks[cb] ) { | 440 | if ( call->session->callbacks[cb] ) { |
456 | LOGGER_DEBUG("Invoking callback function: %d", cb); | 441 | LOGGER_DEBUG("Invoking callback function: %d", cb); |
457 | call->session->callbacks[cb] ( call->session->av, call ); | 442 | if ( call->session->callbacks[cb] ( call->session->av, call ) != 0 ) { |
443 | LOGGER_WARNING("Callback handling failed, sending error"); | ||
444 | goto FAILURE; | ||
445 | } | ||
446 | |||
447 | return 0; | ||
458 | } | 448 | } |
449 | |||
450 | FAILURE: | ||
451 | /* If no callback present or error happened while handling, | ||
452 | * an error message will be send to friend | ||
453 | */ | ||
454 | |||
455 | call->error = msi_HandleError; | ||
456 | return -1; | ||
459 | } | 457 | } |
460 | static MSICall *get_call ( MSISession *session, uint32_t friend_id ) | 458 | static MSICall *get_call ( MSISession *session, uint32_t friend_id ) |
461 | { | 459 | { |
@@ -563,7 +561,7 @@ void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data) | |||
563 | if (call == NULL) | 561 | if (call == NULL) |
564 | return; | 562 | return; |
565 | 563 | ||
566 | invoke_callback(call, msi_OnPeerTimeout); | 564 | invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */ |
567 | kill_call(call); | 565 | kill_call(call); |
568 | } | 566 | } |
569 | break; | 567 | break; |
@@ -572,205 +570,120 @@ void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data) | |||
572 | break; | 570 | break; |
573 | } | 571 | } |
574 | } | 572 | } |
575 | int handle_recv_invite ( MSICall *call, const MSIMessage *msg ) | 573 | void handle_push ( MSICall *call, const MSIMessage *msg ) |
576 | { | 574 | { |
577 | assert(call); | 575 | assert(call); |
578 | 576 | ||
579 | MSISession* session = call->session; | 577 | MSISession* session = call->session; |
580 | 578 | ||
581 | LOGGER_DEBUG("Session: %p Handling 'invite' friend: %d", call->session, call->friend_id); | 579 | LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_id); |
582 | 580 | ||
583 | if (!msg->capabilities.exists) { | 581 | if (!msg->capabilities.exists) { |
584 | LOGGER_WARNING("Session: %p Invalid capabilities on 'invite'"); | 582 | LOGGER_WARNING("Session: %p Invalid capabilities on 'push'"); |
585 | call->error = msi_InvalidMessage; | 583 | call->error = msi_InvalidMessage; |
586 | return -1; | 584 | goto FAILURE; |
587 | } | 585 | } |
588 | 586 | ||
589 | MSIMessage response; | 587 | if (call->state != msi_CallActive) { |
590 | response.response.exists = true; | ||
591 | |||
592 | if ( call->state == msi_CallRequesting ) { | ||
593 | /* The rare glare case. | ||
594 | * Send starting and wait for starting by the other side. | ||
595 | * The peer will do the same. | ||
596 | * When you receive starting from peer send started. | ||
597 | * Don't notice the app until the start is received. | ||
598 | */ | ||
599 | |||
600 | LOGGER_DEBUG("Glare detected!"); | ||
601 | |||
602 | if (!msg->vfpsz.exists) { | 588 | if (!msg->vfpsz.exists) { |
603 | LOGGER_WARNING("Session: %p Invalid mvfpsz on 'invite'"); | 589 | LOGGER_WARNING("Session: %p Invalid vfpsz on 'push'"); |
604 | call->error = msi_InvalidMessage; | 590 | call->error = msi_InvalidMessage; |
605 | return -1; | 591 | goto FAILURE; |
606 | } | 592 | } |
607 | 593 | ||
608 | call->peer_capabilities = msg->capabilities.value; | 594 | /* Sending video frame piece size is ignored when call is active */ |
609 | call->peer_vfpsz = ntohs(msg->vfpsz.value); | 595 | call->peer_vfpsz = ntohs(msg->vfpsz.value); |
596 | } | ||
597 | |||
598 | |||
599 | switch (call->state) { | ||
600 | case msi_CallInactive: { | ||
601 | LOGGER_INFO("Friend is calling us"); | ||
602 | |||
603 | /* Call requested */ | ||
604 | call->peer_capabilities = msg->capabilities.value; | ||
605 | call->state = msi_CallRequested; | ||
606 | |||
607 | if ( invoke_callback(call, msi_OnInvite) == -1 ) | ||
608 | goto FAILURE; | ||
609 | |||
610 | } break; | ||
610 | 611 | ||
611 | /* Send response */ | 612 | case msi_CallActive: { |
612 | response.response.value = resp_starting; | 613 | /* Only act if capabilities changed */ |
613 | send_message ( call->session->messenger, call->friend_id, &response ); | 614 | if ( call->peer_capabilities != msg->capabilities.value) { |
614 | 615 | LOGGER_INFO("Friend is changing capabilities"); | |
615 | return 0; | 616 | |
616 | } else if ( call->state == msi_CallActive ) { | 617 | call->peer_capabilities = msg->capabilities.value; |
617 | /* Changing capabilities. | 618 | if ( invoke_callback(call, msi_OnCapabilities) == -1 ) |
618 | * We send starting but no response is expected. | 619 | goto FAILURE; |
619 | * WARNING: if start is sent call is terminated with an error | 620 | } |
620 | */ | 621 | } break; |
621 | LOGGER_DEBUG("Peer is changing capabilities"); | ||
622 | |||
623 | /* Send response */ | ||
624 | response.response.value = resp_starting; | ||
625 | send_message ( call->session->messenger, call->friend_id, &response ); | ||
626 | 622 | ||
627 | if ( call->peer_capabilities != msg->capabilities.value) { | 623 | case msi_CallRequesting: { |
628 | /* Only invoke callback if capabilities changed */ | 624 | LOGGER_INFO("Friend answered our call"); |
625 | |||
626 | /* Call started */ | ||
629 | call->peer_capabilities = msg->capabilities.value; | 627 | call->peer_capabilities = msg->capabilities.value; |
630 | invoke_callback(call, msi_OnCapabilities); | 628 | call->state = msi_CallActive; |
631 | } | 629 | |
630 | if ( invoke_callback(call, msi_OnStart) == -1 ) | ||
631 | goto FAILURE; | ||
632 | } break; | ||
632 | 633 | ||
633 | return 0; | 634 | case msi_CallRequested: { |
634 | } | 635 | /* Consecutive pushes during initialization state are ignored */ |
635 | 636 | LOGGER_WARNING("Consecutive push"); | |
636 | if (!msg->vfpsz.exists) { | 637 | } break; |
637 | LOGGER_WARNING("Session: %p Invalid mvfpsz on 'invite'"); | ||
638 | call->error = msi_InvalidMessage; | ||
639 | return -1; | ||
640 | } | 638 | } |
641 | 639 | ||
642 | call->peer_capabilities = msg->capabilities.value; | 640 | return; |
643 | call->peer_vfpsz = ntohs(msg->vfpsz.value); | ||
644 | call->state = msi_CallRequested; | ||
645 | |||
646 | /* Send response */ | ||
647 | response.response.value = resp_ringing; | ||
648 | send_message ( call->session->messenger, call->friend_id, &response ); | ||
649 | |||
650 | |||
651 | invoke_callback(call, msi_OnInvite); | ||
652 | return 0; | ||
653 | } | ||
654 | int handle_recv_start ( MSICall *call, const MSIMessage *msg ) | ||
655 | { | ||
656 | assert(call); | ||
657 | |||
658 | if ( call->state != msi_CallRequested || call->state != msi_CallRequesting ) { | ||
659 | LOGGER_WARNING("Session: %p Invalid call state on 'start'"); | ||
660 | call->error = msi_InvalidState; | ||
661 | return -1; | ||
662 | } | ||
663 | |||
664 | (void)msg; | ||
665 | |||
666 | LOGGER_DEBUG("Session: %p Handling 'start', friend id: %d", call->session, call->friend_id ); | ||
667 | |||
668 | call->state = msi_CallActive; | ||
669 | invoke_callback(call, msi_OnStart); | ||
670 | |||
671 | return 0; | ||
672 | } | ||
673 | int handle_recv_reject ( MSICall *call, const MSIMessage *msg ) | ||
674 | { | ||
675 | assert(call); | ||
676 | |||
677 | (void)msg; | ||
678 | |||
679 | if ( call->state != msi_CallRequesting ) { | ||
680 | LOGGER_WARNING("Session: %p Invalid call state on 'reject'"); | ||
681 | call->error = msi_InvalidState; | ||
682 | return -1; | ||
683 | } | ||
684 | 641 | ||
685 | LOGGER_DEBUG("Session: %p Handling 'reject', friend id: %u", call->session, call->friend_id); | 642 | FAILURE: |
686 | 643 | send_error(call->session->messenger, call->friend_id, call->error); | |
687 | invoke_callback(call, msi_OnReject); | ||
688 | kill_call(call); | 644 | kill_call(call); |
689 | |||
690 | return 0; | ||
691 | } | ||
692 | int handle_recv_end ( MSICall *call, const MSIMessage *msg ) | ||
693 | { | ||
694 | assert(call); | ||
695 | |||
696 | (void)msg; | ||
697 | |||
698 | LOGGER_DEBUG("Session: %p Handling 'end', friend id: %d", call->session, call->friend_id); | ||
699 | |||
700 | invoke_callback(call, msi_OnEnd); | ||
701 | kill_call ( call ); | ||
702 | |||
703 | return 0; | ||
704 | } | 645 | } |
705 | int handle_recv_ringing ( MSICall *call, const MSIMessage *msg ) | 646 | void handle_pop ( MSICall *call, const MSIMessage *msg ) |
706 | { | 647 | { |
707 | assert(call); | 648 | assert(call); |
708 | 649 | ||
709 | (void)msg; | 650 | LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_id); |
710 | |||
711 | if ( call->state != msi_CallRequesting ) { | ||
712 | LOGGER_WARNING("Session: %p Invalid call state on 'ringing'"); | ||
713 | call->error = msi_InvalidState; | ||
714 | return -1; | ||
715 | } | ||
716 | |||
717 | LOGGER_DEBUG("Session: %p Handling 'ringing' friend id: %d", call->session, call->friend_id ); | ||
718 | |||
719 | invoke_callback(call, msi_OnRinging); | ||
720 | return 0; | ||
721 | } | ||
722 | int handle_recv_starting ( MSICall *call, const MSIMessage *msg ) | ||
723 | { | ||
724 | assert(call); | ||
725 | 651 | ||
726 | if ( call->state == msi_CallActive ) { | 652 | /* callback errors are ignored */ |
727 | LOGGER_DEBUG("Capabilities change confirmed"); | ||
728 | return 0; | ||
729 | } else if ( call->state != msi_CallRequested || call->state != msi_CallRequesting ) { | ||
730 | LOGGER_WARNING("Session: %p Invalid call state on 'starting'"); | ||
731 | call->error = msi_InvalidState; | ||
732 | return -1; | ||
733 | } | ||
734 | 653 | ||
735 | if (call->state == msi_CallRequesting) { | 654 | if (msg->error.exists) { |
736 | if (!msg->capabilities.exists) { | 655 | LOGGER_WARNING("Friend detected an error: %d", msg->error.value); |
737 | LOGGER_WARNING("Session: %p Invalid capabilities on 'starting'"); | 656 | call->error = msg->error.value; |
738 | call->error = msi_InvalidParam; | 657 | invoke_callback(call, msi_OnError); |
739 | return -1; | ||
740 | } | ||
741 | 658 | ||
742 | if (!msg->vfpsz.exists) { | 659 | } else switch (call->state) { |
743 | LOGGER_WARNING("Session: %p Invalid mvfpsz on 'invite'"); | 660 | case msi_CallInactive: { |
744 | call->error = msi_InvalidParam; | 661 | LOGGER_ERROR("Handling what should be impossible case"); |
745 | return -1; | 662 | abort(); |
746 | } | 663 | } break; |
747 | 664 | ||
748 | call->peer_capabilities = msg->capabilities.value; | 665 | case msi_CallActive: { |
749 | call->peer_vfpsz = ntohs(msg->vfpsz.value); | 666 | /* Hangup */ |
750 | call->state = msi_CallActive; | 667 | LOGGER_INFO("Friend hung up on us"); |
668 | invoke_callback(call, msi_OnEnd); | ||
669 | } break; | ||
751 | 670 | ||
752 | invoke_callback(call, msi_OnStart); | 671 | case msi_CallRequesting: { |
672 | /* Reject */ | ||
673 | LOGGER_INFO("Friend rejected our call"); | ||
674 | invoke_callback(call, msi_OnEnd); | ||
675 | } break; | ||
676 | |||
677 | case msi_CallRequested: { | ||
678 | /* Cancel */ | ||
679 | LOGGER_INFO("Friend canceled call invite"); | ||
680 | invoke_callback(call, msi_OnEnd); | ||
681 | } break; | ||
753 | } | 682 | } |
754 | /* Otherwise it's a glare case so don't start until 'start' is recved */ | ||
755 | |||
756 | /* Send start in either case (glare or normal) */ | ||
757 | MSIMessage msg_start; | ||
758 | msg_start.request.exists = true; | ||
759 | msg_start.request.value = requ_start; | ||
760 | send_message ( call->session->messenger, call->friend_id, &msg_start ); | ||
761 | 683 | ||
762 | return 0; | 684 | kill_call ( call ); |
763 | } | ||
764 | int handle_recv_error ( MSICall *call, const MSIMessage *msg ) | ||
765 | { | ||
766 | assert(call); | ||
767 | |||
768 | LOGGER_DEBUG("Session: %p Handling 'error' friend id: %d", call->session, call->friend_id ); | ||
769 | |||
770 | call->error = msg->error.value; | ||
771 | invoke_callback(call, msi_OnError); | ||
772 | 685 | ||
773 | return 0; | 686 | return; |
774 | } | 687 | } |
775 | void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object ) | 688 | void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object ) |
776 | { | 689 | { |
@@ -779,8 +692,6 @@ void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint1 | |||
779 | MSISession *session = object; | 692 | MSISession *session = object; |
780 | MSIMessage msg; | 693 | MSIMessage msg; |
781 | 694 | ||
782 | int rc = 0; | ||
783 | |||
784 | if ( msg_parse_in ( &msg, data, length ) == -1 ) { | 695 | if ( msg_parse_in ( &msg, data, length ) == -1 ) { |
785 | LOGGER_WARNING("Error parsing message"); | 696 | LOGGER_WARNING("Error parsing message"); |
786 | send_error(m, friend_id, msi_InvalidMessage); | 697 | send_error(m, friend_id, msi_InvalidMessage); |
@@ -792,7 +703,7 @@ void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint1 | |||
792 | MSICall *call = get_call(session, friend_id); | 703 | MSICall *call = get_call(session, friend_id); |
793 | 704 | ||
794 | if (call == NULL) { | 705 | if (call == NULL) { |
795 | if (msg.request.value != requ_invite) { | 706 | if (msg.request.value != requ_push) { |
796 | send_error(m, friend_id, msi_StrayMessage); | 707 | send_error(m, friend_id, msi_StrayMessage); |
797 | return; | 708 | return; |
798 | } | 709 | } |
@@ -805,49 +716,8 @@ void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint1 | |||
805 | } | 716 | } |
806 | 717 | ||
807 | 718 | ||
808 | /* Now handle message */ | 719 | if (msg.request.value == requ_push) |
809 | if ( msg.request.exists ) { /* Handle request */ | 720 | handle_push(call, &msg); |
810 | switch (msg.request.value) { | 721 | else |
811 | case requ_invite: | 722 | handle_pop(call, &msg); /* always kills the call */ |
812 | rc = handle_recv_invite ( call, &msg ); | 723 | } \ No newline at end of file |
813 | break; | ||
814 | |||
815 | case requ_start: | ||
816 | rc = handle_recv_start ( call, &msg ); | ||
817 | break; | ||
818 | |||
819 | case requ_reject: | ||
820 | rc = handle_recv_reject ( call, &msg ); | ||
821 | break; | ||
822 | |||
823 | case requ_end: | ||
824 | rc = handle_recv_end ( call, &msg ); | ||
825 | break; | ||
826 | } | ||
827 | } else if ( msg.response.exists ) { /* Handle response */ | ||
828 | switch (msg.response.value) { | ||
829 | case resp_ringing: | ||
830 | rc = handle_recv_ringing ( call, &msg ); | ||
831 | break; | ||
832 | |||
833 | case resp_starting: | ||
834 | rc = handle_recv_starting ( call, &msg ); | ||
835 | break; | ||
836 | } | ||
837 | } else if (msg.error.exists) { | ||
838 | handle_recv_error ( call, &msg ); | ||
839 | rc = -1; | ||
840 | } else { | ||
841 | LOGGER_WARNING("Invalid message; none of the request, response or error!"); | ||
842 | call->error = msi_InvalidMessage; | ||
843 | rc = -1; | ||
844 | } | ||
845 | |||
846 | |||
847 | if (rc == -1) { | ||
848 | if (call->error != msi_ErrorNone) | ||
849 | send_error(m, friend_id, call->error); | ||
850 | |||
851 | kill_call(call); | ||
852 | } | ||
853 | } | ||
diff --git a/toxav/msi.h b/toxav/msi.h index a55d8567..783d3928 100644 --- a/toxav/msi.h +++ b/toxav/msi.h | |||
@@ -42,6 +42,7 @@ typedef enum { | |||
42 | msi_StrayMessage, | 42 | msi_StrayMessage, |
43 | msi_SystemError, | 43 | msi_SystemError, |
44 | msi_ErrUndisclosed, | 44 | msi_ErrUndisclosed, |
45 | msi_HandleError, | ||
45 | } MSIError; | 46 | } MSIError; |
46 | 47 | ||
47 | /** | 48 | /** |
@@ -70,9 +71,7 @@ typedef enum { | |||
70 | */ | 71 | */ |
71 | typedef enum { | 72 | typedef enum { |
72 | msi_OnInvite, /* Incoming call */ | 73 | msi_OnInvite, /* Incoming call */ |
73 | msi_OnRinging, /* When peer is ready to accept/reject the call */ | ||
74 | msi_OnStart, /* Call (RTP transmission) started */ | 74 | msi_OnStart, /* Call (RTP transmission) started */ |
75 | msi_OnReject, /* The side that was invited rejected the call */ | ||
76 | msi_OnEnd, /* Call that was active ended */ | 75 | msi_OnEnd, /* Call that was active ended */ |
77 | msi_OnError, /* On protocol error */ | 76 | msi_OnError, /* On protocol error */ |
78 | msi_OnPeerTimeout, /* Peer timed out; stop the call */ | 77 | msi_OnPeerTimeout, /* Peer timed out; stop the call */ |
@@ -100,9 +99,12 @@ typedef struct MSICall_s { | |||
100 | 99 | ||
101 | 100 | ||
102 | /** | 101 | /** |
103 | * Msi callback type. 'agent' is a pointer to ToxAv | 102 | * Msi callback type. 'agent' is a pointer to ToxAv. |
103 | * Expected return on success is 0, if any other number is | ||
104 | * returned the call is considered errored and will be handled | ||
105 | * as such which means it will be terminated without any notice. | ||
104 | */ | 106 | */ |
105 | typedef void ( *MSICallbackType ) ( void *agent, MSICall* call); | 107 | typedef int ( *MSICallbackType ) ( void *agent, MSICall* call); |
106 | 108 | ||
107 | /** | 109 | /** |
108 | * Control session struct. Please do not modify outside msi.c | 110 | * Control session struct. Please do not modify outside msi.c |
@@ -117,7 +119,7 @@ typedef struct MSISession_s { | |||
117 | Messenger *messenger; | 119 | Messenger *messenger; |
118 | 120 | ||
119 | pthread_mutex_t mutex[1]; | 121 | pthread_mutex_t mutex[1]; |
120 | MSICallbackType callbacks[8]; | 122 | MSICallbackType callbacks[7]; |
121 | } MSISession; | 123 | } MSISession; |
122 | 124 | ||
123 | /** | 125 | /** |
@@ -151,11 +153,6 @@ int msi_hangup ( MSICall* call ); | |||
151 | int msi_answer ( MSICall* call, uint8_t capabilities ); | 153 | int msi_answer ( MSICall* call, uint8_t capabilities ); |
152 | 154 | ||
153 | /** | 155 | /** |
154 | * Reject incoming call. NOTE: 'call' will be freed | ||
155 | */ | ||
156 | int msi_reject ( MSICall* call ); | ||
157 | |||
158 | /** | ||
159 | * Change capabilities of the call. | 156 | * Change capabilities of the call. |
160 | */ | 157 | */ |
161 | int msi_change_capabilities ( MSICall* call, uint8_t capabilities ); | 158 | int msi_change_capabilities ( MSICall* call, uint8_t capabilities ); |
diff --git a/toxav/rtp.c b/toxav/rtp.c index 396e0202..8319c7dc 100644 --- a/toxav/rtp.c +++ b/toxav/rtp.c | |||
@@ -478,12 +478,24 @@ void rtp_kill ( RTPSession *session ) | |||
478 | free ( session ); | 478 | free ( session ); |
479 | } | 479 | } |
480 | 480 | ||
481 | int rtp_register_for_receiving(RTPSession* session) | 481 | int rtp_start_receiving(RTPSession* session) |
482 | { | 482 | { |
483 | if (session == NULL) | ||
484 | return 0; | ||
485 | |||
483 | return custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, | 486 | return custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, |
484 | rtp_handle_packet, session); | 487 | rtp_handle_packet, session); |
485 | } | 488 | } |
486 | 489 | ||
490 | int rtp_stop_receiving(RTPSession* session) | ||
491 | { | ||
492 | if (session == NULL) | ||
493 | return 0; | ||
494 | |||
495 | return custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix, | ||
496 | NULL, NULL); | ||
497 | } | ||
498 | |||
487 | int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) | 499 | int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length ) |
488 | { | 500 | { |
489 | RTPMessage *msg = rtp_new_message (session, data, length); | 501 | RTPMessage *msg = rtp_new_message (session, data, length); |
diff --git a/toxav/rtp.h b/toxav/rtp.h index e3b38a8e..2950941b 100644 --- a/toxav/rtp.h +++ b/toxav/rtp.h | |||
@@ -119,7 +119,12 @@ void rtp_kill ( RTPSession* session ); | |||
119 | /** | 119 | /** |
120 | * By default rtp is not in receiving state | 120 | * By default rtp is not in receiving state |
121 | */ | 121 | */ |
122 | int rtp_register_for_receiving (RTPSession *session); | 122 | int rtp_start_receiving (RTPSession *session); |
123 | |||
124 | /** | ||
125 | * Pause rtp receiving mode. | ||
126 | */ | ||
127 | int rtp_stop_receiving (RTPSession *session); | ||
123 | 128 | ||
124 | /** | 129 | /** |
125 | * Sends msg to RTPSession::dest | 130 | * Sends msg to RTPSession::dest |
diff --git a/toxav/toxav.c b/toxav/toxav.c index 584b3898..5054d399 100644 --- a/toxav/toxav.c +++ b/toxav/toxav.c | |||
@@ -42,6 +42,7 @@ enum { | |||
42 | 42 | ||
43 | typedef struct ToxAVCall_s | 43 | typedef struct ToxAVCall_s |
44 | { | 44 | { |
45 | ToxAV* av; | ||
45 | pthread_mutex_t mutex_control[1]; | 46 | pthread_mutex_t mutex_control[1]; |
46 | pthread_mutex_t mutex_encoding_audio[1]; | 47 | pthread_mutex_t mutex_encoding_audio[1]; |
47 | pthread_mutex_t mutex_encoding_video[1]; | 48 | pthread_mutex_t mutex_encoding_video[1]; |
@@ -55,6 +56,8 @@ typedef struct ToxAVCall_s | |||
55 | uint32_t s_audio_b; /* Sending audio bitrate */ | 56 | uint32_t s_audio_b; /* Sending audio bitrate */ |
56 | uint32_t s_video_b; /* Sending video bitrate */ | 57 | uint32_t s_video_b; /* Sending video bitrate */ |
57 | 58 | ||
59 | uint8_t last_capabilities; | ||
60 | |||
58 | struct ToxAVCall_s *prev; | 61 | struct ToxAVCall_s *prev; |
59 | struct ToxAVCall_s *next; | 62 | struct ToxAVCall_s *next; |
60 | } ToxAVCall; | 63 | } ToxAVCall; |
@@ -83,22 +86,20 @@ struct toxAV | |||
83 | }; | 86 | }; |
84 | 87 | ||
85 | 88 | ||
86 | void i_callback_invite(void* toxav_inst, MSICall* call); | 89 | int callback_invite(void* toxav_inst, MSICall* call); |
87 | void i_callback_ringing(void* toxav_inst, MSICall* call); | 90 | int callback_start(void* toxav_inst, MSICall* call); |
88 | void i_callback_start(void* toxav_inst, MSICall* call); | 91 | int callback_end(void* toxav_inst, MSICall* call); |
89 | void i_callback_end(void* toxav_inst, MSICall* call); | 92 | int callback_error(void* toxav_inst, MSICall* call); |
90 | void i_callback_error(void* toxav_inst, MSICall* call); | 93 | int callback_capabilites(void* toxav_inst, MSICall* call); |
91 | void i_callback_capabilites(void* toxav_inst, MSICall* call); | ||
92 | 94 | ||
93 | TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities); | 95 | TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities); |
94 | ToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number); | 96 | ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); |
95 | ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number); | 97 | ToxAVCall* call_get(ToxAV* av, uint32_t friend_number); |
96 | void i_toxav_remove_call(ToxAV* av, uint32_t friend_number); | 98 | void call_remove(ToxAVCall* call); |
97 | ToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error); | 99 | bool audio_bitrate_invalid(uint32_t bitrate); |
98 | bool i_toxav_audio_bitrate_invalid(uint32_t bitrate); | 100 | bool video_bitrate_invalid(uint32_t bitrate); |
99 | bool i_toxav_video_bitrate_invalid(uint32_t bitrate); | 101 | bool call_prepare_transmission(ToxAVCall* call); |
100 | bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call); | 102 | void call_kill_transmission(ToxAVCall* call); |
101 | void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number); | ||
102 | 103 | ||
103 | 104 | ||
104 | 105 | ||
@@ -136,14 +137,12 @@ ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error) | |||
136 | av->interval = 200; | 137 | av->interval = 200; |
137 | av->msi->av = av; | 138 | av->msi->av = av; |
138 | 139 | ||
139 | msi_register_callback(av->msi, i_callback_invite, msi_OnInvite); | 140 | msi_register_callback(av->msi, callback_invite, msi_OnInvite); |
140 | msi_register_callback(av->msi, i_callback_ringing, msi_OnRinging); | 141 | msi_register_callback(av->msi, callback_start, msi_OnStart); |
141 | msi_register_callback(av->msi, i_callback_start, msi_OnStart); | 142 | msi_register_callback(av->msi, callback_end, msi_OnEnd); |
142 | msi_register_callback(av->msi, i_callback_end, msi_OnReject); | 143 | msi_register_callback(av->msi, callback_error, msi_OnError); |
143 | msi_register_callback(av->msi, i_callback_end, msi_OnEnd); | 144 | msi_register_callback(av->msi, callback_error, msi_OnPeerTimeout); |
144 | msi_register_callback(av->msi, i_callback_error, msi_OnError); | 145 | msi_register_callback(av->msi, callback_capabilites, msi_OnCapabilities); |
145 | msi_register_callback(av->msi, i_callback_error, msi_OnPeerTimeout); | ||
146 | msi_register_callback(av->msi, i_callback_capabilites, msi_OnCapabilities); | ||
147 | 146 | ||
148 | 147 | ||
149 | if (error) | 148 | if (error) |
@@ -166,7 +165,16 @@ void toxav_kill(ToxAV* av) | |||
166 | return; | 165 | return; |
167 | 166 | ||
168 | msi_kill(av->msi); | 167 | msi_kill(av->msi); |
169 | /* TODO iterate over calls */ | 168 | |
169 | /* Msi kill will hang up all calls so just clean these calls */ | ||
170 | if (av->calls) { | ||
171 | ToxAVCall* it = call_get(av, av->calls_head); | ||
172 | for (; it; it = it->next) { | ||
173 | call_kill_transmission(it); | ||
174 | call_remove(it); /* This will eventually free av->calls */ | ||
175 | } | ||
176 | } | ||
177 | |||
170 | free(av); | 178 | free(av); |
171 | } | 179 | } |
172 | 180 | ||
@@ -208,20 +216,20 @@ void toxav_iteration(ToxAV* av) | |||
208 | 216 | ||
209 | bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) | 217 | bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error) |
210 | { | 218 | { |
211 | ToxAVCall* call = i_toxav_init_call(av, friend_number, error); | 219 | ToxAVCall* call = call_new(av, friend_number, error); |
212 | if (call == NULL) | 220 | if (call == NULL) |
213 | return false; | 221 | return false; |
214 | 222 | ||
215 | call->s_audio_b = audio_bit_rate; | 223 | call->s_audio_b = audio_bit_rate; |
216 | call->s_video_b = video_bit_rate; | 224 | call->s_video_b = video_bit_rate; |
217 | 225 | ||
218 | uint8_t capabilities = 0; | 226 | call->last_capabilities = msi_CapRAudio | msi_CapRVideo; |
219 | 227 | ||
220 | capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; | 228 | call->last_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; |
221 | capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; | 229 | call->last_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; |
222 | 230 | ||
223 | if (msi_invite(av->msi, &call->msi_call, friend_number, capabilities) != 0) { | 231 | if (msi_invite(av->msi, &call->msi_call, friend_number, call->last_capabilities) != 0) { |
224 | i_toxav_remove_call(av, friend_number); | 232 | call_remove(call); |
225 | if (error) | 233 | if (error) |
226 | *error = TOXAV_ERR_CALL_MALLOC; | 234 | *error = TOXAV_ERR_CALL_MALLOC; |
227 | return false; | 235 | return false; |
@@ -244,14 +252,14 @@ bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, ui | |||
244 | goto END; | 252 | goto END; |
245 | } | 253 | } |
246 | 254 | ||
247 | if ((audio_bit_rate && i_toxav_audio_bitrate_invalid(audio_bit_rate)) | 255 | if ((audio_bit_rate && audio_bitrate_invalid(audio_bit_rate)) |
248 | ||(video_bit_rate && i_toxav_video_bitrate_invalid(video_bit_rate)) | 256 | ||(video_bit_rate && video_bitrate_invalid(video_bit_rate)) |
249 | ) { | 257 | ) { |
250 | rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; | 258 | rc = TOXAV_ERR_CALL_INVALID_BIT_RATE; |
251 | goto END; | 259 | goto END; |
252 | } | 260 | } |
253 | 261 | ||
254 | ToxAVCall* call = i_toxav_get_call(av, friend_number); | 262 | ToxAVCall* call = call_get(av, friend_number); |
255 | if (call == NULL) { | 263 | if (call == NULL) { |
256 | rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; | 264 | rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; |
257 | goto END; | 265 | goto END; |
@@ -260,12 +268,12 @@ bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, ui | |||
260 | call->s_audio_b = audio_bit_rate; | 268 | call->s_audio_b = audio_bit_rate; |
261 | call->s_video_b = video_bit_rate; | 269 | call->s_video_b = video_bit_rate; |
262 | 270 | ||
263 | uint8_t capabilities = 0; | 271 | call->last_capabilities = msi_CapRAudio | msi_CapRVideo; |
264 | 272 | ||
265 | capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; | 273 | call->last_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0; |
266 | capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; | 274 | call->last_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0; |
267 | 275 | ||
268 | if (msi_answer(call->msi_call, capabilities) != 0) | 276 | if (msi_answer(call->msi_call, call->last_capabilities) != 0) |
269 | rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; /* the only reason for msi_answer to fail */ | 277 | rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; /* the only reason for msi_answer to fail */ |
270 | 278 | ||
271 | 279 | ||
@@ -292,7 +300,7 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co | |||
292 | } | 300 | } |
293 | 301 | ||
294 | 302 | ||
295 | ToxAVCall* call = i_toxav_get_call(av, friend_number); | 303 | ToxAVCall* call = call_get(av, friend_number); |
296 | if (call == NULL) { | 304 | if (call == NULL) { |
297 | rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; | 305 | rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL; |
298 | goto END; | 306 | goto END; |
@@ -302,29 +310,53 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co | |||
302 | switch (control) | 310 | switch (control) |
303 | { | 311 | { |
304 | case TOXAV_CALL_CONTROL_RESUME: { | 312 | case TOXAV_CALL_CONTROL_RESUME: { |
305 | 313 | if (call->msi_call->self_capabilities == 0 && | |
314 | call->last_capabilities ) { | ||
315 | /* Only act if paused and had media transfer active before */ | ||
316 | |||
317 | if (msi_change_capabilities(call->msi_call, call->last_capabilities) == -1) | ||
318 | return false; | ||
319 | |||
320 | rtp_start_receiving(call->rtps[audio_index]); | ||
321 | rtp_start_receiving(call->rtps[video_index]); | ||
322 | } | ||
306 | } break; | 323 | } break; |
307 | 324 | ||
308 | case TOXAV_CALL_CONTROL_PAUSE: { | 325 | case TOXAV_CALL_CONTROL_PAUSE: { |
309 | 326 | if (call->msi_call->self_capabilities) { | |
327 | /* Only act if not already paused */ | ||
328 | |||
329 | call->last_capabilities = call->msi_call->self_capabilities; | ||
330 | |||
331 | if (msi_change_capabilities(call->msi_call, 0) == -1 ) | ||
332 | return false; | ||
333 | |||
334 | rtp_stop_receiving(call->rtps[audio_index]); | ||
335 | rtp_stop_receiving(call->rtps[video_index]); | ||
336 | } | ||
310 | } break; | 337 | } break; |
311 | 338 | ||
312 | case TOXAV_CALL_CONTROL_CANCEL: { | 339 | case TOXAV_CALL_CONTROL_CANCEL: { |
313 | if (call->msi_call->state == msi_CallActive | 340 | /* Hang up */ |
314 | || call->msi_call->state == msi_CallRequesting) { | 341 | msi_hangup(call->msi_call); |
315 | /* Hang up */ | ||
316 | msi_hangup(call->msi_call); | ||
317 | } else if (call->msi_call->state == msi_CallRequested) { | ||
318 | /* Reject the call */ | ||
319 | msi_reject(call->msi_call); | ||
320 | } | ||
321 | 342 | ||
322 | // No mather the case, terminate the call | 343 | /* No mather the case, terminate the call */ |
323 | i_toxav_remove_call(av, friend_number); | 344 | call_remove(call); |
324 | } break; | 345 | } break; |
325 | 346 | ||
326 | case TOXAV_CALL_CONTROL_MUTE_AUDIO: { | 347 | case TOXAV_CALL_CONTROL_MUTE_AUDIO: { |
327 | 348 | if (call->msi_call->self_capabilities & msi_CapRAudio || | |
349 | call->msi_call->self_capabilities & msi_CapSAudio) { | ||
350 | |||
351 | uint8_t capabilities = call->msi_call->self_capabilities; | ||
352 | capabilities ^= msi_CapRAudio; | ||
353 | capabilities ^= msi_CapRAudio; | ||
354 | |||
355 | if (msi_change_capabilities(call->msi_call, call->msi_call->self_capabilities) == -1) | ||
356 | return false; | ||
357 | |||
358 | rtp_stop_receiving(call->rtps[audio_index]); | ||
359 | } | ||
328 | } break; | 360 | } break; |
329 | 361 | ||
330 | case TOXAV_CALL_CONTROL_MUTE_VIDEO: { | 362 | case TOXAV_CALL_CONTROL_MUTE_VIDEO: { |
@@ -364,7 +396,7 @@ bool toxav_send_video_frame(ToxAV* av, uint32_t friend_number, uint16_t width, u | |||
364 | goto END; | 396 | goto END; |
365 | } | 397 | } |
366 | 398 | ||
367 | call = i_toxav_get_call(av, friend_number); | 399 | call = call_get(av, friend_number); |
368 | if (call == NULL) { | 400 | if (call == NULL) { |
369 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; | 401 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; |
370 | goto END; | 402 | goto END; |
@@ -461,7 +493,7 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc | |||
461 | goto END; | 493 | goto END; |
462 | } | 494 | } |
463 | 495 | ||
464 | call = i_toxav_get_call(av, friend_number); | 496 | call = call_get(av, friend_number); |
465 | if (call == NULL) { | 497 | if (call == NULL) { |
466 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; | 498 | rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL; |
467 | goto END; | 499 | goto END; |
@@ -526,19 +558,16 @@ void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb* | |||
526 | * :: Internal | 558 | * :: Internal |
527 | * | 559 | * |
528 | ******************************************************************************/ | 560 | ******************************************************************************/ |
529 | /** TODO: | 561 | /** TODO: |
530 | * - If crutial callback not present send error. | ||
531 | * - Error handling by return values from callbacks and setting 'error'. | ||
532 | */ | 562 | */ |
533 | void i_callback_invite(void* toxav_inst, MSICall* call) | 563 | int callback_invite(void* toxav_inst, MSICall* call) |
534 | { | 564 | { |
535 | ToxAV* toxav = toxav_inst; | 565 | ToxAV* toxav = toxav_inst; |
536 | 566 | ||
537 | ToxAVCall* av_call = i_toxav_init_call(toxav, call->friend_id, NULL); | 567 | ToxAVCall* av_call = call_new(toxav, call->friend_id, NULL); |
538 | if (av_call == NULL) { | 568 | if (av_call == NULL) { |
539 | LOGGER_WARNING("Failed to start call, rejecting..."); | 569 | LOGGER_WARNING("Failed to initialize call..."); |
540 | msi_reject(call); | 570 | return -1; |
541 | return; | ||
542 | } | 571 | } |
543 | 572 | ||
544 | call->av_call = av_call; | 573 | call->av_call = av_call; |
@@ -547,55 +576,59 @@ void i_callback_invite(void* toxav_inst, MSICall* call) | |||
547 | if (toxav->ccb.first) | 576 | if (toxav->ccb.first) |
548 | toxav->ccb.first(toxav, call->friend_id, call->peer_capabilities & msi_CapSAudio, | 577 | toxav->ccb.first(toxav, call->friend_id, call->peer_capabilities & msi_CapSAudio, |
549 | call->peer_capabilities & msi_CapSVideo, toxav->ccb.second); | 578 | call->peer_capabilities & msi_CapSVideo, toxav->ccb.second); |
579 | |||
580 | return 0; | ||
550 | } | 581 | } |
551 | 582 | ||
552 | void i_callback_ringing(void* toxav_inst, MSICall* call) | 583 | int callback_start(void* toxav_inst, MSICall* call) |
553 | { | ||
554 | ToxAV* toxav = toxav_inst; | ||
555 | if (toxav->scb.first) | ||
556 | toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_RINGING, toxav->scb.second); | ||
557 | } | ||
558 | |||
559 | void i_callback_start(void* toxav_inst, MSICall* call) | ||
560 | { | 584 | { |
561 | ToxAV* toxav = toxav_inst; | 585 | ToxAV* toxav = toxav_inst; |
562 | 586 | ||
563 | ToxAVCall* av_call = i_toxav_get_call(toxav, call->friend_id); | 587 | ToxAVCall* av_call = call_get(toxav, call->friend_id); |
564 | 588 | ||
565 | if (av_call == NULL || !i_toxav_prepare_transmission(toxav, av_call)) { | 589 | if (av_call == NULL || !call_prepare_transmission(av_call)) { |
566 | /* TODO send error */ | 590 | call_remove(av_call); |
567 | i_toxav_remove_call(toxav, call->friend_id); | 591 | return -1; |
568 | return; | ||
569 | } | 592 | } |
570 | 593 | ||
571 | TOXAV_CALL_STATE state = capabilities_to_state(av_call->msi_call->peer_capabilities); | 594 | TOXAV_CALL_STATE state = capabilities_to_state(av_call->msi_call->peer_capabilities); |
572 | 595 | ||
573 | if (toxav->scb.first) | 596 | if (toxav->scb.first) |
574 | toxav->scb.first(toxav, call->friend_id, state, toxav->scb.second); | 597 | toxav->scb.first(toxav, call->friend_id, state, toxav->scb.second); |
598 | |||
599 | return 0; | ||
575 | } | 600 | } |
576 | 601 | ||
577 | void i_callback_end(void* toxav_inst, MSICall* call) | 602 | int callback_end(void* toxav_inst, MSICall* call) |
578 | { | 603 | { |
579 | ToxAV* toxav = toxav_inst; | 604 | ToxAV* toxav = toxav_inst; |
580 | 605 | ||
581 | i_toxav_kill_transmission(toxav, call->friend_id); | 606 | call_kill_transmission(call->av_call); |
582 | i_toxav_remove_call(toxav, call->friend_id); | 607 | call_remove(call->av_call); |
583 | 608 | ||
584 | if (toxav->scb.first) | 609 | if (toxav->scb.first) |
585 | toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_END, toxav->scb.second); | 610 | toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_END, toxav->scb.second); |
611 | |||
612 | return 0; | ||
586 | } | 613 | } |
587 | 614 | ||
588 | void i_callback_error(void* toxav_inst, MSICall* call) | 615 | int callback_error(void* toxav_inst, MSICall* call) |
589 | { | 616 | { |
590 | ToxAV* toxav = toxav_inst; | 617 | ToxAV* toxav = toxav_inst; |
591 | if (toxav->scb.first) | 618 | if (toxav->scb.first) |
592 | toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR, toxav->scb.second); | 619 | toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR, toxav->scb.second); |
620 | |||
621 | return 0; | ||
593 | } | 622 | } |
594 | 623 | ||
595 | void i_callback_capabilites(void* toxav_inst, MSICall* call) | 624 | int callback_capabilites(void* toxav_inst, MSICall* call) |
596 | { | 625 | { |
597 | ToxAV* toxav = toxav_inst; | 626 | ToxAV* toxav = toxav_inst; |
598 | /* TODO handle this */ | 627 | if (toxav->scb.first) |
628 | toxav->scb.first(toxav, call->friend_id, | ||
629 | capabilities_to_state(call->peer_capabilities), toxav->scb.second); | ||
630 | |||
631 | return 0; | ||
599 | } | 632 | } |
600 | 633 | ||
601 | TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities) | 634 | TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities) |
@@ -610,32 +643,64 @@ TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities) | |||
610 | return TOXAV_CALL_STATE_PAUSED; | 643 | return TOXAV_CALL_STATE_PAUSED; |
611 | } | 644 | } |
612 | 645 | ||
613 | ToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number) | 646 | bool audio_bitrate_invalid(uint32_t bitrate) |
614 | { | 647 | { |
615 | if (av->calls == NULL || av->calls_tail < friend_number) | 648 | /* Opus RFC 6716 section-2.1.1 dictates the following: |
616 | return NULL; | 649 | * Opus supports all bitrates from 6 kbit/s to 510 kbit/s. |
617 | 650 | */ | |
618 | return av->calls[friend_number]; | 651 | return bitrate < 6 || bitrate > 510; |
619 | } | 652 | } |
620 | 653 | ||
621 | ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number) | 654 | bool video_bitrate_invalid(uint32_t bitrate) |
622 | { | 655 | { |
623 | ToxAVCall* rc = calloc(sizeof(ToxAVCall), 1); | 656 | /* TODO: If anyone knows the answer to this one please fill it up */ |
657 | return false; | ||
658 | } | ||
659 | |||
660 | ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error) | ||
661 | { | ||
662 | TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK; | ||
663 | ToxAVCall* call = NULL; | ||
624 | 664 | ||
625 | if (rc == NULL) | 665 | if (m_friend_exists(av->m, friend_number) == 0) { |
626 | return NULL; | 666 | rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND; |
667 | goto END; | ||
668 | } | ||
627 | 669 | ||
628 | rc->friend_id = friend_number; | 670 | if (m_get_friend_connectionstatus(av->m, friend_number) != 1) { |
671 | rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED; | ||
672 | goto END; | ||
673 | } | ||
629 | 674 | ||
630 | if (create_recursive_mutex(rc->mutex_control) != 0) { | 675 | if (call_get(av, friend_number) != NULL) { |
631 | free(rc); | 676 | rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL; |
632 | return NULL; | 677 | goto END; |
633 | } | 678 | } |
634 | 679 | ||
635 | if (create_recursive_mutex(rc->mutex_do) != 0) { | 680 | |
636 | pthread_mutex_destroy(rc->mutex_control); | 681 | call = calloc(sizeof(ToxAVCall), 1); |
637 | free(rc); | 682 | |
638 | return NULL; | 683 | if (call == NULL) { |
684 | rc = TOXAV_ERR_CALL_MALLOC; | ||
685 | goto END; | ||
686 | } | ||
687 | |||
688 | call->av = av; | ||
689 | call->friend_id = friend_number; | ||
690 | |||
691 | if (create_recursive_mutex(call->mutex_control) != 0) { | ||
692 | free(call); | ||
693 | call = NULL; | ||
694 | rc = TOXAV_ERR_CALL_MALLOC; | ||
695 | goto END; | ||
696 | } | ||
697 | |||
698 | if (create_recursive_mutex(call->mutex_do) != 0) { | ||
699 | pthread_mutex_destroy(call->mutex_control); | ||
700 | free(call); | ||
701 | call = NULL; | ||
702 | rc = TOXAV_ERR_CALL_MALLOC; | ||
703 | goto END; | ||
639 | } | 704 | } |
640 | 705 | ||
641 | 706 | ||
@@ -643,10 +708,12 @@ ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number) | |||
643 | av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1); | 708 | av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1); |
644 | 709 | ||
645 | if (av->calls == NULL) { | 710 | if (av->calls == NULL) { |
646 | pthread_mutex_destroy(rc->mutex_control); | 711 | pthread_mutex_destroy(call->mutex_control); |
647 | pthread_mutex_destroy(rc->mutex_do); | 712 | pthread_mutex_destroy(call->mutex_do); |
648 | free(rc); | 713 | free(call); |
649 | return NULL; | 714 | call = NULL; |
715 | rc = TOXAV_ERR_CALL_MALLOC; | ||
716 | goto END; | ||
650 | } | 717 | } |
651 | 718 | ||
652 | av->calls_tail = av->calls_head = friend_number; | 719 | av->calls_tail = av->calls_head = friend_number; |
@@ -655,10 +722,12 @@ ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number) | |||
655 | void* tmp = realloc(av->calls, sizeof(ToxAVCall*) * friend_number + 1); | 722 | void* tmp = realloc(av->calls, sizeof(ToxAVCall*) * friend_number + 1); |
656 | 723 | ||
657 | if (tmp == NULL) { | 724 | if (tmp == NULL) { |
658 | pthread_mutex_destroy(rc->mutex_control); | 725 | pthread_mutex_destroy(call->mutex_control); |
659 | pthread_mutex_destroy(rc->mutex_do); | 726 | pthread_mutex_destroy(call->mutex_do); |
660 | free(rc); | 727 | free(call); |
661 | return NULL; | 728 | call = NULL; |
729 | rc = TOXAV_ERR_CALL_MALLOC; | ||
730 | goto END; | ||
662 | } | 731 | } |
663 | 732 | ||
664 | av->calls = tmp; | 733 | av->calls = tmp; |
@@ -668,105 +737,41 @@ ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number) | |||
668 | for (; i < friend_number; i ++) | 737 | for (; i < friend_number; i ++) |
669 | av->calls[i] = NULL; | 738 | av->calls[i] = NULL; |
670 | 739 | ||
671 | rc->prev = av->calls[av->calls_tail]; | 740 | call->prev = av->calls[av->calls_tail]; |
672 | av->calls[av->calls_tail]->next = rc; | 741 | av->calls[av->calls_tail]->next = call; |
673 | 742 | ||
674 | av->calls_tail = friend_number; | 743 | av->calls_tail = friend_number; |
675 | 744 | ||
676 | } else if (av->calls_head > friend_number) { /* Inserting at front */ | 745 | } else if (av->calls_head > friend_number) { /* Inserting at front */ |
677 | rc->next = av->calls[av->calls_head]; | 746 | call->next = av->calls[av->calls_head]; |
678 | av->calls[av->calls_head]->prev = rc; | 747 | av->calls[av->calls_head]->prev = call; |
679 | av->calls_head = friend_number; | 748 | av->calls_head = friend_number; |
680 | } | 749 | } |
681 | 750 | ||
682 | av->calls[friend_number] = rc; | 751 | av->calls[friend_number] = call; |
683 | return rc; | ||
684 | } | ||
685 | |||
686 | void i_toxav_remove_call(ToxAV* av, uint32_t friend_number) | ||
687 | { | ||
688 | ToxAVCall* tc = i_toxav_get_call(av, friend_number); | ||
689 | |||
690 | if (tc == NULL) | ||
691 | return; | ||
692 | |||
693 | ToxAVCall* prev = tc->prev; | ||
694 | ToxAVCall* next = tc->next; | ||
695 | |||
696 | pthread_mutex_destroy(tc->mutex_control); | ||
697 | pthread_mutex_destroy(tc->mutex_do); | ||
698 | |||
699 | free(tc); | ||
700 | |||
701 | if (prev) | ||
702 | prev->next = next; | ||
703 | else if (next) | ||
704 | av->calls_head = next->friend_id; | ||
705 | else goto CLEAR; | ||
706 | 752 | ||
707 | if (next) | 753 | END: |
708 | next->prev = prev; | ||
709 | else if (prev) | ||
710 | av->calls_tail = prev->friend_id; | ||
711 | else goto CLEAR; | ||
712 | |||
713 | av->calls[friend_number] = NULL; | ||
714 | return; | ||
715 | |||
716 | CLEAR: | ||
717 | av->calls_head = av->calls_tail = 0; | ||
718 | free(av->calls); | ||
719 | av->calls = NULL; | ||
720 | } | ||
721 | |||
722 | ToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error) | ||
723 | { | ||
724 | TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK; | ||
725 | ToxAVCall* call = NULL; | ||
726 | |||
727 | if (m_friend_exists(av->m, friend_number) == 0) { | ||
728 | rc = TOXAV_ERR_CALL_FRIEND_NOT_FOUND; | ||
729 | goto END; | ||
730 | } | ||
731 | |||
732 | if (m_get_friend_connectionstatus(av->m, friend_number) != 1) { | ||
733 | rc = TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED; | ||
734 | goto END; | ||
735 | } | ||
736 | |||
737 | if (i_toxav_get_call(av, friend_number) != NULL) { | ||
738 | rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL; | ||
739 | goto END; | ||
740 | } | ||
741 | |||
742 | call = i_toxav_add_call(av, friend_number); | ||
743 | if (call == NULL) { | ||
744 | rc = TOXAV_ERR_CALL_MALLOC; | ||
745 | } | ||
746 | |||
747 | END: | ||
748 | if (error) | 754 | if (error) |
749 | *error = rc; | 755 | *error = rc; |
750 | 756 | ||
751 | return call; | 757 | return call; |
752 | } | 758 | } |
753 | 759 | ||
754 | bool i_toxav_audio_bitrate_invalid(uint32_t bitrate) | 760 | ToxAVCall* call_get(ToxAV* av, uint32_t friend_number) |
755 | { | 761 | { |
756 | /* Opus RFC 6716 section-2.1.1 dictates the following: | 762 | if (av->calls == NULL || av->calls_tail < friend_number) |
757 | * Opus supports all bitrates from 6 kbit/s to 510 kbit/s. | 763 | return NULL; |
758 | */ | 764 | |
759 | return bitrate < 6 || bitrate > 510; | 765 | return av->calls[friend_number]; |
760 | } | ||
761 | |||
762 | bool i_toxav_video_bitrate_invalid(uint32_t bitrate) | ||
763 | { | ||
764 | /* TODO: If anyone knows the answer to this one please fill it up */ | ||
765 | return false; | ||
766 | } | 766 | } |
767 | 767 | ||
768 | bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call) | 768 | bool call_prepare_transmission(ToxAVCall* call) |
769 | { | 769 | { |
770 | if (call == NULL) | ||
771 | return false; | ||
772 | |||
773 | ToxAV* av = call->av; | ||
774 | |||
770 | if (!av->acb.first && !av->vcb.first) | 775 | if (!av->acb.first && !av->vcb.first) |
771 | /* It makes no sense to have CSession without callbacks */ | 776 | /* It makes no sense to have CSession without callbacks */ |
772 | return false; | 777 | return false; |
@@ -830,7 +835,7 @@ bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call) | |||
830 | goto FAILURE; | 835 | goto FAILURE; |
831 | } | 836 | } |
832 | 837 | ||
833 | rtp_register_for_receiving(call->rtps[audio_index]); | 838 | rtp_start_receiving(call->rtps[audio_index]); |
834 | } | 839 | } |
835 | } | 840 | } |
836 | 841 | ||
@@ -854,8 +859,8 @@ bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call) | |||
854 | LOGGER_WARNING("Failed to enable video receiving!"); | 859 | LOGGER_WARNING("Failed to enable video receiving!"); |
855 | goto FAILURE; | 860 | goto FAILURE; |
856 | } | 861 | } |
857 | 862 | ||
858 | rtp_register_for_receiving(call->rtps[audio_index]); | 863 | rtp_start_receiving(call->rtps[audio_index]); |
859 | } | 864 | } |
860 | } | 865 | } |
861 | 866 | ||
@@ -863,7 +868,7 @@ bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call) | |||
863 | pthread_mutex_unlock(call->mutex_control); | 868 | pthread_mutex_unlock(call->mutex_control); |
864 | return true; | 869 | return true; |
865 | 870 | ||
866 | FAILURE: | 871 | FAILURE: |
867 | rtp_kill(call->rtps[audio_index]); | 872 | rtp_kill(call->rtps[audio_index]); |
868 | call->rtps[audio_index] = NULL; | 873 | call->rtps[audio_index] = NULL; |
869 | rtp_kill(call->rtps[video_index]); | 874 | rtp_kill(call->rtps[video_index]); |
@@ -878,25 +883,19 @@ FAILURE: | |||
878 | pthread_mutex_unlock(call->mutex_control); | 883 | pthread_mutex_unlock(call->mutex_control); |
879 | return false; | 884 | return false; |
880 | 885 | ||
881 | MUTEX_INIT_ERROR: | 886 | MUTEX_INIT_ERROR: |
882 | pthread_mutex_unlock(call->mutex_control); | 887 | pthread_mutex_unlock(call->mutex_control); |
883 | LOGGER_ERROR("Mutex initialization failed!\n"); | 888 | LOGGER_ERROR("Mutex initialization failed!\n"); |
884 | return false; | 889 | return false; |
885 | } | 890 | } |
886 | 891 | ||
887 | void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number) | 892 | void call_kill_transmission(ToxAVCall* call) |
888 | { | 893 | { |
889 | ToxAVCall* call = i_toxav_get_call(av, friend_number); | 894 | if (call == NULL || call->active == 0) |
890 | if (!call) | ||
891 | return; | 895 | return; |
892 | 896 | ||
893 | pthread_mutex_lock(call->mutex_control); | 897 | pthread_mutex_lock(call->mutex_control); |
894 | 898 | ||
895 | if (!call->active) { | ||
896 | pthread_mutex_unlock(call->mutex_control); | ||
897 | return; | ||
898 | } | ||
899 | |||
900 | call->active = 0; | 899 | call->active = 0; |
901 | 900 | ||
902 | pthread_mutex_lock(call->mutex_encoding_audio); | 901 | pthread_mutex_lock(call->mutex_encoding_audio); |
@@ -918,4 +917,41 @@ void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number) | |||
918 | pthread_mutex_destroy(call->mutex_do); | 917 | pthread_mutex_destroy(call->mutex_do); |
919 | 918 | ||
920 | pthread_mutex_unlock(call->mutex_control); | 919 | pthread_mutex_unlock(call->mutex_control); |
920 | } | ||
921 | |||
922 | void call_remove(ToxAVCall* call) | ||
923 | { | ||
924 | if (call == NULL) | ||
925 | return; | ||
926 | |||
927 | uint32_t friend_id = call->friend_id; | ||
928 | ToxAV* av = call->av; | ||
929 | |||
930 | ToxAVCall* prev = call->prev; | ||
931 | ToxAVCall* next = call->next; | ||
932 | |||
933 | pthread_mutex_destroy(call->mutex_control); | ||
934 | pthread_mutex_destroy(call->mutex_do); | ||
935 | |||
936 | free(call); | ||
937 | |||
938 | if (prev) | ||
939 | prev->next = next; | ||
940 | else if (next) | ||
941 | av->calls_head = next->friend_id; | ||
942 | else goto CLEAR; | ||
943 | |||
944 | if (next) | ||
945 | next->prev = prev; | ||
946 | else if (prev) | ||
947 | av->calls_tail = prev->friend_id; | ||
948 | else goto CLEAR; | ||
949 | |||
950 | av->calls[friend_id] = NULL; | ||
951 | return; | ||
952 | |||
953 | CLEAR: | ||
954 | av->calls_head = av->calls_tail = 0; | ||
955 | free(av->calls); | ||
956 | av->calls = NULL; | ||
921 | } \ No newline at end of file | 957 | } \ No newline at end of file |
diff --git a/toxav/toxav.h b/toxav/toxav.h index c1c6019c..5e82faa6 100644 --- a/toxav/toxav.h +++ b/toxav/toxav.h | |||
@@ -187,11 +187,6 @@ bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, ui | |||
187 | ******************************************************************************/ | 187 | ******************************************************************************/ |
188 | typedef enum TOXAV_CALL_STATE { | 188 | typedef enum TOXAV_CALL_STATE { |
189 | /** | 189 | /** |
190 | * The friend's client is aware of the call. This happens after calling | ||
191 | * toxav_call and the initial call request has been received. | ||
192 | */ | ||
193 | TOXAV_CALL_STATE_RINGING, | ||
194 | /** | ||
195 | * Not sending anything. Either the friend requested that this client stops | 190 | * Not sending anything. Either the friend requested that this client stops |
196 | * sending anything, or the client turned off both audio and video by setting | 191 | * sending anything, or the client turned off both audio and video by setting |
197 | * the respective bit rates to 0. | 192 | * the respective bit rates to 0. |