mirror of
https://github.com/irungentoo/toxcore.git
synced 2024-03-22 13:30:51 +08:00
New msi protocol
This commit is contained in:
parent
efe31ec92f
commit
29601feb76
|
@ -36,19 +36,11 @@ void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, TOXAV_CALL_STATE s
|
|||
{
|
||||
printf("Handling CALL STATE callback: ");
|
||||
|
||||
if (((CallControl*)user_data)->ringing)
|
||||
((CallControl*)user_data)->ringing = false;
|
||||
|
||||
if (((CallControl*)user_data)->paused)
|
||||
((CallControl*)user_data)->paused = false;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case TOXAV_CALL_STATE_RINGING: {
|
||||
printf("Ringing");
|
||||
((CallControl*)user_data)->ringing = true;
|
||||
} break;
|
||||
|
||||
{
|
||||
case TOXAV_CALL_STATE_NOT_SENDING: {
|
||||
printf("Not sending");
|
||||
((CallControl*)user_data)->sending = false;
|
||||
|
@ -230,6 +222,7 @@ int main (int argc, char** argv)
|
|||
exit(1); \
|
||||
} \
|
||||
BobCC.incoming = false; \
|
||||
BobCC.sending = true; /* There is no more start callback when answering */\
|
||||
} \
|
||||
else if (AliceCC.sending && BobCC.sending) { \
|
||||
/* TODO rtp */ \
|
||||
|
@ -238,6 +231,7 @@ int main (int argc, char** argv)
|
|||
\
|
||||
TOXAV_ERR_CALL_CONTROL rc; \
|
||||
toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc); \
|
||||
AliceCC.ended = true; /* There is no more end callback when hanging up */\
|
||||
\
|
||||
if (rc != TOXAV_ERR_CALL_CONTROL_OK) { \
|
||||
printf("toxav_call_control failed: %d\n", rc); \
|
||||
|
@ -252,13 +246,13 @@ int main (int argc, char** argv)
|
|||
}
|
||||
|
||||
printf("\nTrying regular call (Audio and Video)...\n");
|
||||
REGULAR_CALL_FLOW(48, 4000);
|
||||
// REGULAR_CALL_FLOW(48, 4000);
|
||||
|
||||
printf("\nTrying regular call (Audio only)...\n");
|
||||
REGULAR_CALL_FLOW(48, 0);
|
||||
// REGULAR_CALL_FLOW(48, 0);
|
||||
|
||||
printf("\nTrying regular call (Video only)...\n");
|
||||
REGULAR_CALL_FLOW(0, 4000);
|
||||
// REGULAR_CALL_FLOW(0, 4000);
|
||||
|
||||
#undef REGULAR_CALL_FLOW
|
||||
|
||||
|
@ -292,7 +286,7 @@ int main (int argc, char** argv)
|
|||
}
|
||||
}
|
||||
|
||||
while (!AliceCC.ended || !BobCC.ended)
|
||||
while (!AliceCC.ended)
|
||||
iterate(Bsn, AliceAV, BobAV);
|
||||
|
||||
printf("Success!\n");
|
||||
|
|
500
toxav/msi.c
500
toxav/msi.c
|
@ -37,8 +37,6 @@
|
|||
|
||||
#define MSI_MAXMSG_SIZE 256
|
||||
|
||||
/* TODO send error on any calloc or etc */
|
||||
|
||||
/**
|
||||
* Protocol:
|
||||
*
|
||||
|
@ -47,24 +45,17 @@
|
|||
|
||||
typedef enum {
|
||||
IDRequest = 1,
|
||||
IDResponse,
|
||||
IDError,
|
||||
IDCapabilities,
|
||||
IDVFPSZ,
|
||||
|
||||
} MSIHeaderID;
|
||||
|
||||
typedef enum {
|
||||
requ_invite,
|
||||
requ_start,
|
||||
requ_reject,
|
||||
requ_end,
|
||||
} MSIRequest;
|
||||
|
||||
typedef enum {
|
||||
resp_ringing,
|
||||
resp_starting,
|
||||
} MSIResponse;
|
||||
requ_push,
|
||||
requ_pop,
|
||||
} MSIRequest;
|
||||
|
||||
|
||||
#define GENERIC_HEADER(header, val_type) \
|
||||
|
@ -75,7 +66,6 @@ typedef struct { \
|
|||
|
||||
|
||||
GENERIC_HEADER ( Request, MSIRequest );
|
||||
GENERIC_HEADER ( Response, MSIResponse );
|
||||
GENERIC_HEADER ( Error, MSIError );
|
||||
GENERIC_HEADER ( Capabilities, uint8_t );
|
||||
GENERIC_HEADER ( VFPSZ, uint16_t );
|
||||
|
@ -83,29 +73,24 @@ GENERIC_HEADER ( VFPSZ, uint16_t );
|
|||
|
||||
typedef struct {
|
||||
MSIHeaderRequest request;
|
||||
MSIHeaderResponse response;
|
||||
MSIHeaderError error;
|
||||
MSIHeaderCapabilities capabilities;
|
||||
MSIHeaderVFPSZ vfpsz; /* Video frame piece size. NOTE: Value must be in network b-order */
|
||||
} MSIMessage;
|
||||
|
||||
|
||||
void msg_init (MSIMessage *dest, MSIRequest request);
|
||||
int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length );
|
||||
uint8_t *msg_parse_header_out ( MSIHeaderID id, uint8_t *dest, const void *value, uint8_t value_len, uint16_t *length );
|
||||
int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg );
|
||||
int send_error ( Messenger* m, uint32_t friend_id, MSIError error );
|
||||
static void invoke_callback(MSICall* call, MSICallbackID cb);
|
||||
static int invoke_callback(MSICall* call, MSICallbackID cb);
|
||||
static MSICall *get_call ( MSISession *session, uint32_t friend_id );
|
||||
MSICall *new_call ( MSISession *session, uint32_t friend_id );
|
||||
void kill_call ( MSICall *call );
|
||||
void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data);
|
||||
int handle_recv_invite ( MSICall *call, const MSIMessage *msg );
|
||||
int handle_recv_start ( MSICall *call, const MSIMessage *msg );
|
||||
int handle_recv_reject ( MSICall *call, const MSIMessage *msg );
|
||||
int handle_recv_end ( MSICall *call, const MSIMessage *msg );
|
||||
int handle_recv_ringing ( MSICall *call, const MSIMessage *msg );
|
||||
int handle_recv_starting ( MSICall *call, const MSIMessage *msg );
|
||||
int handle_recv_error ( MSICall *call, const MSIMessage *msg );
|
||||
void handle_push ( MSICall *call, const MSIMessage *msg );
|
||||
void handle_pop ( MSICall *call, const MSIMessage *msg );
|
||||
void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object );
|
||||
|
||||
|
||||
|
@ -157,13 +142,12 @@ int msi_kill ( MSISession *session )
|
|||
pthread_mutex_lock(session->mutex);
|
||||
|
||||
if (session->calls) {
|
||||
MSIMessage msg_end;
|
||||
msg_end.request.exists = true;
|
||||
msg_end.request.value = requ_end;
|
||||
MSIMessage msg;
|
||||
msg_init(&msg, requ_pop);
|
||||
|
||||
MSICall* it = get_call(session, session->calls_head);
|
||||
for (; it; it = it->next) {
|
||||
send_message(session->messenger, it->friend_id, &msg_end);
|
||||
send_message(session->messenger, it->friend_id, &msg);
|
||||
kill_call(it); /* This will eventually free session->calls */
|
||||
}
|
||||
}
|
||||
|
@ -191,17 +175,16 @@ int msi_invite ( MSISession *session, MSICall **call, uint32_t friend_id, uint8_
|
|||
|
||||
(*call)->self_capabilities = capabilities;
|
||||
|
||||
MSIMessage msg_invite;
|
||||
msg_invite.request.exists = true;
|
||||
msg_invite.request.value = requ_invite;
|
||||
MSIMessage msg;
|
||||
msg_init(&msg, requ_push);
|
||||
|
||||
msg_invite.capabilities.exists = true;
|
||||
msg_invite.capabilities.value = capabilities;
|
||||
msg.capabilities.exists = true;
|
||||
msg.capabilities.value = capabilities;
|
||||
|
||||
msg_invite.vfpsz.exists = true;
|
||||
msg_invite.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE);
|
||||
msg.vfpsz.exists = true;
|
||||
msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE);
|
||||
|
||||
send_message ( (*call)->session->messenger, (*call)->friend_id, &msg_invite );
|
||||
send_message ( (*call)->session->messenger, (*call)->friend_id, &msg );
|
||||
|
||||
(*call)->state = msi_CallRequesting;
|
||||
|
||||
|
@ -212,10 +195,10 @@ int msi_hangup ( MSICall* call )
|
|||
{
|
||||
LOGGER_DEBUG("Session: %p Hanging up call with friend: %u", call->session, call->friend_id);
|
||||
|
||||
MSIMessage msg_end;
|
||||
msg_end.request.exists = true;
|
||||
msg_end.request.value = requ_end;
|
||||
send_message ( call->session->messenger, call->friend_id, &msg_end );
|
||||
MSIMessage msg;
|
||||
msg_init(&msg, requ_pop);
|
||||
|
||||
send_message ( call->session->messenger, call->friend_id, &msg );
|
||||
|
||||
kill_call(call);
|
||||
return 0;
|
||||
|
@ -225,65 +208,68 @@ int msi_answer ( MSICall* call, uint8_t capabilities )
|
|||
LOGGER_DEBUG("Session: %p Answering call from: %u", call->session, call->friend_id);
|
||||
|
||||
if ( call->state != msi_CallRequested ) {
|
||||
/* Though sending in invalid state will not cause anything wierd
|
||||
* Its better to not do it like a maniac */
|
||||
LOGGER_ERROR("Call is in invalid state!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
call->self_capabilities = capabilities;
|
||||
|
||||
MSIMessage msg_starting;
|
||||
msg_starting.response.exists = true;
|
||||
msg_starting.response.value = resp_starting;
|
||||
MSIMessage msg;
|
||||
msg_init(&msg, requ_push);
|
||||
|
||||
msg_starting.capabilities.exists = true;
|
||||
msg_starting.capabilities.value = capabilities;
|
||||
msg.capabilities.exists = true;
|
||||
msg.capabilities.value = capabilities;
|
||||
|
||||
msg_starting.vfpsz.exists = true;
|
||||
msg_starting.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE);
|
||||
msg.vfpsz.exists = true;
|
||||
msg.vfpsz.value = htons(VIDEOFRAME_PIECE_SIZE);
|
||||
|
||||
send_message ( call->session->messenger, call->friend_id, &msg_starting );
|
||||
send_message ( call->session->messenger, call->friend_id, &msg );
|
||||
|
||||
call->state = msi_CallActive;
|
||||
|
||||
return 0;
|
||||
}
|
||||
int msi_reject ( MSICall* call )
|
||||
int msi_change_capabilities( MSICall* call, uint8_t capabilities )
|
||||
{
|
||||
LOGGER_DEBUG("Session: %p Rejecting call with friend: %u", call->session, call->friend_id);
|
||||
LOGGER_DEBUG("Session: %p Trying to change capabilities to friend %u", call->session, call->friend_id);
|
||||
|
||||
if ( call->state != msi_CallRequested ) {
|
||||
if ( call->state != msi_CallActive ) {
|
||||
/* Sending capabilities change can cause error on other side if
|
||||
* the call is not active since we don't send header 'vfpsz'.
|
||||
* If we were to send 'vfpsz' while call is active it would be
|
||||
* ignored. However, if call is not active peer will expect
|
||||
* the said header on 'push' so that it could handle the call
|
||||
* like new. TODO: explain this better
|
||||
*/
|
||||
LOGGER_ERROR("Call is in invalid state!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
MSIMessage msg_reject;
|
||||
msg_reject.request.exists = true;
|
||||
msg_reject.request.value = requ_reject;
|
||||
|
||||
send_message ( call->session->messenger, call->friend_id, &msg_reject );
|
||||
|
||||
return 0;
|
||||
}
|
||||
int msi_change_csettings( MSICall* call, uint8_t capabilities )
|
||||
{
|
||||
call->self_capabilities = capabilities;
|
||||
|
||||
MSIMessage msg_invite;
|
||||
msg_invite.request.exists = true;
|
||||
msg_invite.request.value = requ_invite;
|
||||
MSIMessage msg;
|
||||
msg_init(&msg, requ_push);
|
||||
|
||||
msg_invite.capabilities.exists = true;
|
||||
msg_invite.capabilities.value = capabilities;
|
||||
msg.capabilities.exists = true;
|
||||
msg.capabilities.value = capabilities;
|
||||
|
||||
send_message ( call->session->messenger, call->friend_id, &msg_invite );
|
||||
send_message ( call->session->messenger, call->friend_id, &msg );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Private functions
|
||||
*/
|
||||
|
||||
void msg_init(MSIMessage* dest, MSIRequest request)
|
||||
{
|
||||
memset(dest, 0, sizeof(*dest));
|
||||
dest->request.exists = true;
|
||||
dest->request.value = request;
|
||||
}
|
||||
int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
|
||||
{
|
||||
/* 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 )
|
|||
LOGGER_ERROR("Invalid end byte");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
memset(dest, 0, sizeof(*dest));
|
||||
|
||||
const uint8_t *it = data;
|
||||
int size_constraint = length;
|
||||
|
||||
|
@ -322,17 +310,10 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
|
|||
switch (*it) {
|
||||
case IDRequest:
|
||||
CHECK_SIZE(it, size_constraint, 1);
|
||||
CHECK_ENUM_HIGH(it, requ_end);
|
||||
CHECK_ENUM_HIGH(it, requ_pop);
|
||||
SET_UINT8(it, dest->request);
|
||||
break;
|
||||
|
||||
case IDResponse:
|
||||
CHECK_SIZE(it, size_constraint, 1);
|
||||
CHECK_ENUM_HIGH(it, resp_starting);
|
||||
SET_UINT8(it, dest->response);
|
||||
it += 3;
|
||||
break;
|
||||
|
||||
case IDError:
|
||||
CHECK_SIZE(it, size_constraint, 1);
|
||||
CHECK_ENUM_HIGH(it, msi_ErrUndisclosed);
|
||||
|
@ -356,6 +337,11 @@ int msg_parse_in ( MSIMessage *dest, const uint8_t *data, uint16_t length )
|
|||
}
|
||||
}
|
||||
|
||||
if (dest->request.exists == false) {
|
||||
LOGGER_ERROR("Invalid request field!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
#undef CHECK_SIZE
|
||||
|
@ -395,17 +381,15 @@ int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg )
|
|||
uint8_t cast = msg->request.value;
|
||||
it = msg_parse_header_out(IDRequest, it, &cast,
|
||||
sizeof(cast), &size);
|
||||
}
|
||||
|
||||
if (msg->response.exists) {
|
||||
uint8_t cast = msg->response.value;
|
||||
it = msg_parse_header_out(IDResponse, it, &cast,
|
||||
sizeof(cast), &size);
|
||||
} else {
|
||||
LOGGER_DEBUG("Must have request field");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msg->error.exists) {
|
||||
it = msg_parse_header_out(IDError, it, &msg->error.value,
|
||||
sizeof(msg->error.value), &size);
|
||||
uint8_t cast = msg->error.value;
|
||||
it = msg_parse_header_out(IDError, it, &cast,
|
||||
sizeof(cast), &size);
|
||||
}
|
||||
|
||||
if (msg->capabilities.exists) {
|
||||
|
@ -418,14 +402,14 @@ int send_message ( Messenger* m, uint32_t friend_id, const MSIMessage *msg )
|
|||
sizeof(msg->vfpsz.value), &size);
|
||||
}
|
||||
|
||||
*it = 0;
|
||||
size ++;
|
||||
|
||||
if ( it == parsed ) {
|
||||
LOGGER_WARNING("Parsing message failed; empty message");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*it = 0;
|
||||
size ++;
|
||||
|
||||
if ( m_msi_packet(m, friend_id, parsed, size) ) {
|
||||
LOGGER_DEBUG("Sent message");
|
||||
return 0;
|
||||
|
@ -440,22 +424,36 @@ int send_error ( Messenger* m, uint32_t friend_id, MSIError error )
|
|||
|
||||
LOGGER_DEBUG("Sending error: %d to friend: %d", error, friend_id);
|
||||
|
||||
MSIMessage msg_error;
|
||||
MSIMessage msg;
|
||||
msg_init(&msg, requ_pop);
|
||||
|
||||
msg_error.error.exists = true;
|
||||
msg_error.error.value = error;
|
||||
msg.error.exists = true;
|
||||
msg.error.value = error;
|
||||
|
||||
send_message ( m, friend_id, &msg_error );
|
||||
send_message ( m, friend_id, &msg );
|
||||
return 0;
|
||||
}
|
||||
static void invoke_callback(MSICall* call, MSICallbackID cb)
|
||||
int invoke_callback(MSICall* call, MSICallbackID cb)
|
||||
{
|
||||
assert(call);
|
||||
|
||||
if ( call->session->callbacks[cb] ) {
|
||||
LOGGER_DEBUG("Invoking callback function: %d", cb);
|
||||
call->session->callbacks[cb] ( call->session->av, call );
|
||||
if ( call->session->callbacks[cb] ( call->session->av, call ) != 0 ) {
|
||||
LOGGER_WARNING("Callback handling failed, sending error");
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FAILURE:
|
||||
/* If no callback present or error happened while handling,
|
||||
* an error message will be send to friend
|
||||
*/
|
||||
|
||||
call->error = msi_HandleError;
|
||||
return -1;
|
||||
}
|
||||
static MSICall *get_call ( MSISession *session, uint32_t friend_id )
|
||||
{
|
||||
|
@ -563,7 +561,7 @@ void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data)
|
|||
if (call == NULL)
|
||||
return;
|
||||
|
||||
invoke_callback(call, msi_OnPeerTimeout);
|
||||
invoke_callback(call, msi_OnPeerTimeout); /* Failure is ignored */
|
||||
kill_call(call);
|
||||
}
|
||||
break;
|
||||
|
@ -572,205 +570,120 @@ void on_peer_status(Messenger *m, int friend_id, uint8_t status, void *data)
|
|||
break;
|
||||
}
|
||||
}
|
||||
int handle_recv_invite ( MSICall *call, const MSIMessage *msg )
|
||||
void handle_push ( MSICall *call, const MSIMessage *msg )
|
||||
{
|
||||
assert(call);
|
||||
|
||||
MSISession* session = call->session;
|
||||
|
||||
LOGGER_DEBUG("Session: %p Handling 'invite' friend: %d", call->session, call->friend_id);
|
||||
LOGGER_DEBUG("Session: %p Handling 'push' friend: %d", call->session, call->friend_id);
|
||||
|
||||
if (!msg->capabilities.exists) {
|
||||
LOGGER_WARNING("Session: %p Invalid capabilities on 'invite'");
|
||||
LOGGER_WARNING("Session: %p Invalid capabilities on 'push'");
|
||||
call->error = msi_InvalidMessage;
|
||||
return -1;
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
MSIMessage response;
|
||||
response.response.exists = true;
|
||||
|
||||
if ( call->state == msi_CallRequesting ) {
|
||||
/* The rare glare case.
|
||||
* Send starting and wait for starting by the other side.
|
||||
* The peer will do the same.
|
||||
* When you receive starting from peer send started.
|
||||
* Don't notice the app until the start is received.
|
||||
*/
|
||||
|
||||
LOGGER_DEBUG("Glare detected!");
|
||||
|
||||
if (call->state != msi_CallActive) {
|
||||
if (!msg->vfpsz.exists) {
|
||||
LOGGER_WARNING("Session: %p Invalid mvfpsz on 'invite'");
|
||||
LOGGER_WARNING("Session: %p Invalid vfpsz on 'push'");
|
||||
call->error = msi_InvalidMessage;
|
||||
return -1;
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
call->peer_capabilities = msg->capabilities.value;
|
||||
/* Sending video frame piece size is ignored when call is active */
|
||||
call->peer_vfpsz = ntohs(msg->vfpsz.value);
|
||||
|
||||
/* Send response */
|
||||
response.response.value = resp_starting;
|
||||
send_message ( call->session->messenger, call->friend_id, &response );
|
||||
|
||||
return 0;
|
||||
} else if ( call->state == msi_CallActive ) {
|
||||
/* Changing capabilities.
|
||||
* We send starting but no response is expected.
|
||||
* WARNING: if start is sent call is terminated with an error
|
||||
*/
|
||||
LOGGER_DEBUG("Peer is changing capabilities");
|
||||
|
||||
/* Send response */
|
||||
response.response.value = resp_starting;
|
||||
send_message ( call->session->messenger, call->friend_id, &response );
|
||||
|
||||
if ( call->peer_capabilities != msg->capabilities.value) {
|
||||
/* Only invoke callback if capabilities changed */
|
||||
}
|
||||
|
||||
|
||||
switch (call->state) {
|
||||
case msi_CallInactive: {
|
||||
LOGGER_INFO("Friend is calling us");
|
||||
|
||||
/* Call requested */
|
||||
call->peer_capabilities = msg->capabilities.value;
|
||||
invoke_callback(call, msi_OnCapabilities);
|
||||
}
|
||||
call->state = msi_CallRequested;
|
||||
|
||||
if ( invoke_callback(call, msi_OnInvite) == -1 )
|
||||
goto FAILURE;
|
||||
|
||||
} break;
|
||||
|
||||
return 0;
|
||||
case msi_CallActive: {
|
||||
/* Only act if capabilities changed */
|
||||
if ( call->peer_capabilities != msg->capabilities.value) {
|
||||
LOGGER_INFO("Friend is changing capabilities");
|
||||
|
||||
call->peer_capabilities = msg->capabilities.value;
|
||||
if ( invoke_callback(call, msi_OnCapabilities) == -1 )
|
||||
goto FAILURE;
|
||||
}
|
||||
} break;
|
||||
|
||||
case msi_CallRequesting: {
|
||||
LOGGER_INFO("Friend answered our call");
|
||||
|
||||
/* Call started */
|
||||
call->peer_capabilities = msg->capabilities.value;
|
||||
call->state = msi_CallActive;
|
||||
|
||||
if ( invoke_callback(call, msi_OnStart) == -1 )
|
||||
goto FAILURE;
|
||||
} break;
|
||||
|
||||
case msi_CallRequested: {
|
||||
/* Consecutive pushes during initialization state are ignored */
|
||||
LOGGER_WARNING("Consecutive push");
|
||||
} break;
|
||||
}
|
||||
|
||||
if (!msg->vfpsz.exists) {
|
||||
LOGGER_WARNING("Session: %p Invalid mvfpsz on 'invite'");
|
||||
call->error = msi_InvalidMessage;
|
||||
return -1;
|
||||
}
|
||||
return;
|
||||
|
||||
call->peer_capabilities = msg->capabilities.value;
|
||||
call->peer_vfpsz = ntohs(msg->vfpsz.value);
|
||||
call->state = msi_CallRequested;
|
||||
|
||||
/* Send response */
|
||||
response.response.value = resp_ringing;
|
||||
send_message ( call->session->messenger, call->friend_id, &response );
|
||||
|
||||
|
||||
invoke_callback(call, msi_OnInvite);
|
||||
return 0;
|
||||
}
|
||||
int handle_recv_start ( MSICall *call, const MSIMessage *msg )
|
||||
{
|
||||
assert(call);
|
||||
|
||||
if ( call->state != msi_CallRequested || call->state != msi_CallRequesting ) {
|
||||
LOGGER_WARNING("Session: %p Invalid call state on 'start'");
|
||||
call->error = msi_InvalidState;
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)msg;
|
||||
|
||||
LOGGER_DEBUG("Session: %p Handling 'start', friend id: %d", call->session, call->friend_id );
|
||||
|
||||
call->state = msi_CallActive;
|
||||
invoke_callback(call, msi_OnStart);
|
||||
|
||||
return 0;
|
||||
}
|
||||
int handle_recv_reject ( MSICall *call, const MSIMessage *msg )
|
||||
{
|
||||
assert(call);
|
||||
|
||||
(void)msg;
|
||||
|
||||
if ( call->state != msi_CallRequesting ) {
|
||||
LOGGER_WARNING("Session: %p Invalid call state on 'reject'");
|
||||
call->error = msi_InvalidState;
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG("Session: %p Handling 'reject', friend id: %u", call->session, call->friend_id);
|
||||
|
||||
invoke_callback(call, msi_OnReject);
|
||||
FAILURE:
|
||||
send_error(call->session->messenger, call->friend_id, call->error);
|
||||
kill_call(call);
|
||||
|
||||
return 0;
|
||||
}
|
||||
int handle_recv_end ( MSICall *call, const MSIMessage *msg )
|
||||
void handle_pop ( MSICall *call, const MSIMessage *msg )
|
||||
{
|
||||
assert(call);
|
||||
|
||||
LOGGER_DEBUG("Session: %p Handling 'pop', friend id: %d", call->session, call->friend_id);
|
||||
|
||||
/* callback errors are ignored */
|
||||
|
||||
if (msg->error.exists) {
|
||||
LOGGER_WARNING("Friend detected an error: %d", msg->error.value);
|
||||
call->error = msg->error.value;
|
||||
invoke_callback(call, msi_OnError);
|
||||
|
||||
} else switch (call->state) {
|
||||
case msi_CallInactive: {
|
||||
LOGGER_ERROR("Handling what should be impossible case");
|
||||
abort();
|
||||
} break;
|
||||
|
||||
case msi_CallActive: {
|
||||
/* Hangup */
|
||||
LOGGER_INFO("Friend hung up on us");
|
||||
invoke_callback(call, msi_OnEnd);
|
||||
} break;
|
||||
|
||||
case msi_CallRequesting: {
|
||||
/* Reject */
|
||||
LOGGER_INFO("Friend rejected our call");
|
||||
invoke_callback(call, msi_OnEnd);
|
||||
} break;
|
||||
|
||||
case msi_CallRequested: {
|
||||
/* Cancel */
|
||||
LOGGER_INFO("Friend canceled call invite");
|
||||
invoke_callback(call, msi_OnEnd);
|
||||
} break;
|
||||
}
|
||||
|
||||
(void)msg;
|
||||
|
||||
LOGGER_DEBUG("Session: %p Handling 'end', friend id: %d", call->session, call->friend_id);
|
||||
|
||||
invoke_callback(call, msi_OnEnd);
|
||||
kill_call ( call );
|
||||
|
||||
return 0;
|
||||
}
|
||||
int handle_recv_ringing ( MSICall *call, const MSIMessage *msg )
|
||||
{
|
||||
assert(call);
|
||||
|
||||
(void)msg;
|
||||
|
||||
if ( call->state != msi_CallRequesting ) {
|
||||
LOGGER_WARNING("Session: %p Invalid call state on 'ringing'");
|
||||
call->error = msi_InvalidState;
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG("Session: %p Handling 'ringing' friend id: %d", call->session, call->friend_id );
|
||||
|
||||
invoke_callback(call, msi_OnRinging);
|
||||
return 0;
|
||||
}
|
||||
int handle_recv_starting ( MSICall *call, const MSIMessage *msg )
|
||||
{
|
||||
assert(call);
|
||||
|
||||
if ( call->state == msi_CallActive ) {
|
||||
LOGGER_DEBUG("Capabilities change confirmed");
|
||||
return 0;
|
||||
} else if ( call->state != msi_CallRequested || call->state != msi_CallRequesting ) {
|
||||
LOGGER_WARNING("Session: %p Invalid call state on 'starting'");
|
||||
call->error = msi_InvalidState;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (call->state == msi_CallRequesting) {
|
||||
if (!msg->capabilities.exists) {
|
||||
LOGGER_WARNING("Session: %p Invalid capabilities on 'starting'");
|
||||
call->error = msi_InvalidParam;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!msg->vfpsz.exists) {
|
||||
LOGGER_WARNING("Session: %p Invalid mvfpsz on 'invite'");
|
||||
call->error = msi_InvalidParam;
|
||||
return -1;
|
||||
}
|
||||
|
||||
call->peer_capabilities = msg->capabilities.value;
|
||||
call->peer_vfpsz = ntohs(msg->vfpsz.value);
|
||||
call->state = msi_CallActive;
|
||||
|
||||
invoke_callback(call, msi_OnStart);
|
||||
}
|
||||
/* Otherwise it's a glare case so don't start until 'start' is recved */
|
||||
|
||||
/* Send start in either case (glare or normal) */
|
||||
MSIMessage msg_start;
|
||||
msg_start.request.exists = true;
|
||||
msg_start.request.value = requ_start;
|
||||
send_message ( call->session->messenger, call->friend_id, &msg_start );
|
||||
|
||||
return 0;
|
||||
}
|
||||
int handle_recv_error ( MSICall *call, const MSIMessage *msg )
|
||||
{
|
||||
assert(call);
|
||||
|
||||
LOGGER_DEBUG("Session: %p Handling 'error' friend id: %d", call->session, call->friend_id );
|
||||
|
||||
call->error = msg->error.value;
|
||||
invoke_callback(call, msi_OnError);
|
||||
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint16_t length, void *object )
|
||||
{
|
||||
|
@ -779,8 +692,6 @@ void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint1
|
|||
MSISession *session = object;
|
||||
MSIMessage msg;
|
||||
|
||||
int rc = 0;
|
||||
|
||||
if ( msg_parse_in ( &msg, data, length ) == -1 ) {
|
||||
LOGGER_WARNING("Error parsing message");
|
||||
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
|
|||
MSICall *call = get_call(session, friend_id);
|
||||
|
||||
if (call == NULL) {
|
||||
if (msg.request.value != requ_invite) {
|
||||
if (msg.request.value != requ_push) {
|
||||
send_error(m, friend_id, msi_StrayMessage);
|
||||
return;
|
||||
}
|
||||
|
@ -805,49 +716,8 @@ void handle_msi_packet ( Messenger *m, int friend_id, const uint8_t *data, uint1
|
|||
}
|
||||
|
||||
|
||||
/* Now handle message */
|
||||
if ( msg.request.exists ) { /* Handle request */
|
||||
switch (msg.request.value) {
|
||||
case requ_invite:
|
||||
rc = handle_recv_invite ( call, &msg );
|
||||
break;
|
||||
|
||||
case requ_start:
|
||||
rc = handle_recv_start ( call, &msg );
|
||||
break;
|
||||
|
||||
case requ_reject:
|
||||
rc = handle_recv_reject ( call, &msg );
|
||||
break;
|
||||
|
||||
case requ_end:
|
||||
rc = handle_recv_end ( call, &msg );
|
||||
break;
|
||||
}
|
||||
} else if ( msg.response.exists ) { /* Handle response */
|
||||
switch (msg.response.value) {
|
||||
case resp_ringing:
|
||||
rc = handle_recv_ringing ( call, &msg );
|
||||
break;
|
||||
|
||||
case resp_starting:
|
||||
rc = handle_recv_starting ( call, &msg );
|
||||
break;
|
||||
}
|
||||
} else if (msg.error.exists) {
|
||||
handle_recv_error ( call, &msg );
|
||||
rc = -1;
|
||||
} else {
|
||||
LOGGER_WARNING("Invalid message; none of the request, response or error!");
|
||||
call->error = msi_InvalidMessage;
|
||||
rc = -1;
|
||||
}
|
||||
|
||||
|
||||
if (rc == -1) {
|
||||
if (call->error != msi_ErrorNone)
|
||||
send_error(m, friend_id, call->error);
|
||||
|
||||
kill_call(call);
|
||||
}
|
||||
}
|
||||
if (msg.request.value == requ_push)
|
||||
handle_push(call, &msg);
|
||||
else
|
||||
handle_pop(call, &msg); /* always kills the call */
|
||||
}
|
17
toxav/msi.h
17
toxav/msi.h
|
@ -42,6 +42,7 @@ typedef enum {
|
|||
msi_StrayMessage,
|
||||
msi_SystemError,
|
||||
msi_ErrUndisclosed,
|
||||
msi_HandleError,
|
||||
} MSIError;
|
||||
|
||||
/**
|
||||
|
@ -70,9 +71,7 @@ typedef enum {
|
|||
*/
|
||||
typedef enum {
|
||||
msi_OnInvite, /* Incoming call */
|
||||
msi_OnRinging, /* When peer is ready to accept/reject the call */
|
||||
msi_OnStart, /* Call (RTP transmission) started */
|
||||
msi_OnReject, /* The side that was invited rejected the call */
|
||||
msi_OnEnd, /* Call that was active ended */
|
||||
msi_OnError, /* On protocol error */
|
||||
msi_OnPeerTimeout, /* Peer timed out; stop the call */
|
||||
|
@ -100,9 +99,12 @@ typedef struct MSICall_s {
|
|||
|
||||
|
||||
/**
|
||||
* Msi callback type. 'agent' is a pointer to ToxAv
|
||||
* Msi callback type. 'agent' is a pointer to ToxAv.
|
||||
* Expected return on success is 0, if any other number is
|
||||
* returned the call is considered errored and will be handled
|
||||
* as such which means it will be terminated without any notice.
|
||||
*/
|
||||
typedef void ( *MSICallbackType ) ( void *agent, MSICall* call);
|
||||
typedef int ( *MSICallbackType ) ( void *agent, MSICall* call);
|
||||
|
||||
/**
|
||||
* Control session struct. Please do not modify outside msi.c
|
||||
|
@ -117,7 +119,7 @@ typedef struct MSISession_s {
|
|||
Messenger *messenger;
|
||||
|
||||
pthread_mutex_t mutex[1];
|
||||
MSICallbackType callbacks[8];
|
||||
MSICallbackType callbacks[7];
|
||||
} MSISession;
|
||||
|
||||
/**
|
||||
|
@ -150,11 +152,6 @@ int msi_hangup ( MSICall* call );
|
|||
*/
|
||||
int msi_answer ( MSICall* call, uint8_t capabilities );
|
||||
|
||||
/**
|
||||
* Reject incoming call. NOTE: 'call' will be freed
|
||||
*/
|
||||
int msi_reject ( MSICall* call );
|
||||
|
||||
/**
|
||||
* Change capabilities of the call.
|
||||
*/
|
||||
|
|
14
toxav/rtp.c
14
toxav/rtp.c
|
@ -478,12 +478,24 @@ void rtp_kill ( RTPSession *session )
|
|||
free ( session );
|
||||
}
|
||||
|
||||
int rtp_register_for_receiving(RTPSession* session)
|
||||
int rtp_start_receiving(RTPSession* session)
|
||||
{
|
||||
if (session == NULL)
|
||||
return 0;
|
||||
|
||||
return custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix,
|
||||
rtp_handle_packet, session);
|
||||
}
|
||||
|
||||
int rtp_stop_receiving(RTPSession* session)
|
||||
{
|
||||
if (session == NULL)
|
||||
return 0;
|
||||
|
||||
return custom_lossy_packet_registerhandler(session->m, session->dest, session->prefix,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
int rtp_send_msg ( RTPSession *session, const uint8_t *data, uint16_t length )
|
||||
{
|
||||
RTPMessage *msg = rtp_new_message (session, data, length);
|
||||
|
|
|
@ -119,7 +119,12 @@ void rtp_kill ( RTPSession* session );
|
|||
/**
|
||||
* By default rtp is not in receiving state
|
||||
*/
|
||||
int rtp_register_for_receiving (RTPSession *session);
|
||||
int rtp_start_receiving (RTPSession *session);
|
||||
|
||||
/**
|
||||
* Pause rtp receiving mode.
|
||||
*/
|
||||
int rtp_stop_receiving (RTPSession *session);
|
||||
|
||||
/**
|
||||
* Sends msg to RTPSession::dest
|
||||
|
|
458
toxav/toxav.c
458
toxav/toxav.c
|
@ -42,6 +42,7 @@ enum {
|
|||
|
||||
typedef struct ToxAVCall_s
|
||||
{
|
||||
ToxAV* av;
|
||||
pthread_mutex_t mutex_control[1];
|
||||
pthread_mutex_t mutex_encoding_audio[1];
|
||||
pthread_mutex_t mutex_encoding_video[1];
|
||||
|
@ -55,6 +56,8 @@ typedef struct ToxAVCall_s
|
|||
uint32_t s_audio_b; /* Sending audio bitrate */
|
||||
uint32_t s_video_b; /* Sending video bitrate */
|
||||
|
||||
uint8_t last_capabilities;
|
||||
|
||||
struct ToxAVCall_s *prev;
|
||||
struct ToxAVCall_s *next;
|
||||
} ToxAVCall;
|
||||
|
@ -83,22 +86,20 @@ struct toxAV
|
|||
};
|
||||
|
||||
|
||||
void i_callback_invite(void* toxav_inst, MSICall* call);
|
||||
void i_callback_ringing(void* toxav_inst, MSICall* call);
|
||||
void i_callback_start(void* toxav_inst, MSICall* call);
|
||||
void i_callback_end(void* toxav_inst, MSICall* call);
|
||||
void i_callback_error(void* toxav_inst, MSICall* call);
|
||||
void i_callback_capabilites(void* toxav_inst, MSICall* call);
|
||||
int callback_invite(void* toxav_inst, MSICall* call);
|
||||
int callback_start(void* toxav_inst, MSICall* call);
|
||||
int callback_end(void* toxav_inst, MSICall* call);
|
||||
int callback_error(void* toxav_inst, MSICall* call);
|
||||
int callback_capabilites(void* toxav_inst, MSICall* call);
|
||||
|
||||
TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities);
|
||||
ToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number);
|
||||
ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number);
|
||||
void i_toxav_remove_call(ToxAV* av, uint32_t friend_number);
|
||||
ToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error);
|
||||
bool i_toxav_audio_bitrate_invalid(uint32_t bitrate);
|
||||
bool i_toxav_video_bitrate_invalid(uint32_t bitrate);
|
||||
bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call);
|
||||
void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number);
|
||||
ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error);
|
||||
ToxAVCall* call_get(ToxAV* av, uint32_t friend_number);
|
||||
void call_remove(ToxAVCall* call);
|
||||
bool audio_bitrate_invalid(uint32_t bitrate);
|
||||
bool video_bitrate_invalid(uint32_t bitrate);
|
||||
bool call_prepare_transmission(ToxAVCall* call);
|
||||
void call_kill_transmission(ToxAVCall* call);
|
||||
|
||||
|
||||
|
||||
|
@ -136,14 +137,12 @@ ToxAV* toxav_new(Tox* tox, TOXAV_ERR_NEW* error)
|
|||
av->interval = 200;
|
||||
av->msi->av = av;
|
||||
|
||||
msi_register_callback(av->msi, i_callback_invite, msi_OnInvite);
|
||||
msi_register_callback(av->msi, i_callback_ringing, msi_OnRinging);
|
||||
msi_register_callback(av->msi, i_callback_start, msi_OnStart);
|
||||
msi_register_callback(av->msi, i_callback_end, msi_OnReject);
|
||||
msi_register_callback(av->msi, i_callback_end, msi_OnEnd);
|
||||
msi_register_callback(av->msi, i_callback_error, msi_OnError);
|
||||
msi_register_callback(av->msi, i_callback_error, msi_OnPeerTimeout);
|
||||
msi_register_callback(av->msi, i_callback_capabilites, msi_OnCapabilities);
|
||||
msi_register_callback(av->msi, callback_invite, msi_OnInvite);
|
||||
msi_register_callback(av->msi, callback_start, msi_OnStart);
|
||||
msi_register_callback(av->msi, callback_end, msi_OnEnd);
|
||||
msi_register_callback(av->msi, callback_error, msi_OnError);
|
||||
msi_register_callback(av->msi, callback_error, msi_OnPeerTimeout);
|
||||
msi_register_callback(av->msi, callback_capabilites, msi_OnCapabilities);
|
||||
|
||||
|
||||
if (error)
|
||||
|
@ -166,7 +165,16 @@ void toxav_kill(ToxAV* av)
|
|||
return;
|
||||
|
||||
msi_kill(av->msi);
|
||||
/* TODO iterate over calls */
|
||||
|
||||
/* Msi kill will hang up all calls so just clean these calls */
|
||||
if (av->calls) {
|
||||
ToxAVCall* it = call_get(av, av->calls_head);
|
||||
for (; it; it = it->next) {
|
||||
call_kill_transmission(it);
|
||||
call_remove(it); /* This will eventually free av->calls */
|
||||
}
|
||||
}
|
||||
|
||||
free(av);
|
||||
}
|
||||
|
||||
|
@ -208,20 +216,20 @@ void toxav_iteration(ToxAV* av)
|
|||
|
||||
bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint32_t video_bit_rate, TOXAV_ERR_CALL* error)
|
||||
{
|
||||
ToxAVCall* call = i_toxav_init_call(av, friend_number, error);
|
||||
ToxAVCall* call = call_new(av, friend_number, error);
|
||||
if (call == NULL)
|
||||
return false;
|
||||
|
||||
call->s_audio_b = audio_bit_rate;
|
||||
call->s_video_b = video_bit_rate;
|
||||
|
||||
uint8_t capabilities = 0;
|
||||
call->last_capabilities = msi_CapRAudio | msi_CapRVideo;
|
||||
|
||||
capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
|
||||
capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
|
||||
call->last_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
|
||||
call->last_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
|
||||
|
||||
if (msi_invite(av->msi, &call->msi_call, friend_number, capabilities) != 0) {
|
||||
i_toxav_remove_call(av, friend_number);
|
||||
if (msi_invite(av->msi, &call->msi_call, friend_number, call->last_capabilities) != 0) {
|
||||
call_remove(call);
|
||||
if (error)
|
||||
*error = TOXAV_ERR_CALL_MALLOC;
|
||||
return false;
|
||||
|
@ -244,14 +252,14 @@ bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, ui
|
|||
goto END;
|
||||
}
|
||||
|
||||
if ((audio_bit_rate && i_toxav_audio_bitrate_invalid(audio_bit_rate))
|
||||
||(video_bit_rate && i_toxav_video_bitrate_invalid(video_bit_rate))
|
||||
if ((audio_bit_rate && audio_bitrate_invalid(audio_bit_rate))
|
||||
||(video_bit_rate && video_bitrate_invalid(video_bit_rate))
|
||||
) {
|
||||
rc = TOXAV_ERR_CALL_INVALID_BIT_RATE;
|
||||
goto END;
|
||||
}
|
||||
|
||||
ToxAVCall* call = i_toxav_get_call(av, friend_number);
|
||||
ToxAVCall* call = call_get(av, friend_number);
|
||||
if (call == NULL) {
|
||||
rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING;
|
||||
goto END;
|
||||
|
@ -260,12 +268,12 @@ bool toxav_answer(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, ui
|
|||
call->s_audio_b = audio_bit_rate;
|
||||
call->s_video_b = video_bit_rate;
|
||||
|
||||
uint8_t capabilities = 0;
|
||||
call->last_capabilities = msi_CapRAudio | msi_CapRVideo;
|
||||
|
||||
capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
|
||||
capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
|
||||
call->last_capabilities |= audio_bit_rate > 0 ? msi_CapSAudio : 0;
|
||||
call->last_capabilities |= video_bit_rate > 0 ? msi_CapSVideo : 0;
|
||||
|
||||
if (msi_answer(call->msi_call, capabilities) != 0)
|
||||
if (msi_answer(call->msi_call, call->last_capabilities) != 0)
|
||||
rc = TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING; /* the only reason for msi_answer to fail */
|
||||
|
||||
|
||||
|
@ -292,7 +300,7 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
|
|||
}
|
||||
|
||||
|
||||
ToxAVCall* call = i_toxav_get_call(av, friend_number);
|
||||
ToxAVCall* call = call_get(av, friend_number);
|
||||
if (call == NULL) {
|
||||
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
|
||||
goto END;
|
||||
|
@ -302,29 +310,53 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
|
|||
switch (control)
|
||||
{
|
||||
case TOXAV_CALL_CONTROL_RESUME: {
|
||||
|
||||
if (call->msi_call->self_capabilities == 0 &&
|
||||
call->last_capabilities ) {
|
||||
/* Only act if paused and had media transfer active before */
|
||||
|
||||
if (msi_change_capabilities(call->msi_call, call->last_capabilities) == -1)
|
||||
return false;
|
||||
|
||||
rtp_start_receiving(call->rtps[audio_index]);
|
||||
rtp_start_receiving(call->rtps[video_index]);
|
||||
}
|
||||
} break;
|
||||
|
||||
case TOXAV_CALL_CONTROL_PAUSE: {
|
||||
|
||||
if (call->msi_call->self_capabilities) {
|
||||
/* Only act if not already paused */
|
||||
|
||||
call->last_capabilities = call->msi_call->self_capabilities;
|
||||
|
||||
if (msi_change_capabilities(call->msi_call, 0) == -1 )
|
||||
return false;
|
||||
|
||||
rtp_stop_receiving(call->rtps[audio_index]);
|
||||
rtp_stop_receiving(call->rtps[video_index]);
|
||||
}
|
||||
} break;
|
||||
|
||||
case TOXAV_CALL_CONTROL_CANCEL: {
|
||||
if (call->msi_call->state == msi_CallActive
|
||||
|| call->msi_call->state == msi_CallRequesting) {
|
||||
/* Hang up */
|
||||
msi_hangup(call->msi_call);
|
||||
} else if (call->msi_call->state == msi_CallRequested) {
|
||||
/* Reject the call */
|
||||
msi_reject(call->msi_call);
|
||||
}
|
||||
/* Hang up */
|
||||
msi_hangup(call->msi_call);
|
||||
|
||||
// No mather the case, terminate the call
|
||||
i_toxav_remove_call(av, friend_number);
|
||||
/* No mather the case, terminate the call */
|
||||
call_remove(call);
|
||||
} break;
|
||||
|
||||
case TOXAV_CALL_CONTROL_MUTE_AUDIO: {
|
||||
|
||||
if (call->msi_call->self_capabilities & msi_CapRAudio ||
|
||||
call->msi_call->self_capabilities & msi_CapSAudio) {
|
||||
|
||||
uint8_t capabilities = call->msi_call->self_capabilities;
|
||||
capabilities ^= msi_CapRAudio;
|
||||
capabilities ^= msi_CapRAudio;
|
||||
|
||||
if (msi_change_capabilities(call->msi_call, call->msi_call->self_capabilities) == -1)
|
||||
return false;
|
||||
|
||||
rtp_stop_receiving(call->rtps[audio_index]);
|
||||
}
|
||||
} break;
|
||||
|
||||
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
|
|||
goto END;
|
||||
}
|
||||
|
||||
call = i_toxav_get_call(av, friend_number);
|
||||
call = call_get(av, friend_number);
|
||||
if (call == NULL) {
|
||||
rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
|
||||
goto END;
|
||||
|
@ -461,7 +493,7 @@ bool toxav_send_audio_frame(ToxAV* av, uint32_t friend_number, const int16_t* pc
|
|||
goto END;
|
||||
}
|
||||
|
||||
call = i_toxav_get_call(av, friend_number);
|
||||
call = call_get(av, friend_number);
|
||||
if (call == NULL) {
|
||||
rc = TOXAV_ERR_SEND_FRAME_FRIEND_NOT_IN_CALL;
|
||||
goto END;
|
||||
|
@ -526,19 +558,16 @@ void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb*
|
|||
* :: Internal
|
||||
*
|
||||
******************************************************************************/
|
||||
/** TODO:
|
||||
* - If crutial callback not present send error.
|
||||
* - Error handling by return values from callbacks and setting 'error'.
|
||||
/** TODO:
|
||||
*/
|
||||
void i_callback_invite(void* toxav_inst, MSICall* call)
|
||||
int callback_invite(void* toxav_inst, MSICall* call)
|
||||
{
|
||||
ToxAV* toxav = toxav_inst;
|
||||
|
||||
ToxAVCall* av_call = i_toxav_init_call(toxav, call->friend_id, NULL);
|
||||
ToxAVCall* av_call = call_new(toxav, call->friend_id, NULL);
|
||||
if (av_call == NULL) {
|
||||
LOGGER_WARNING("Failed to start call, rejecting...");
|
||||
msi_reject(call);
|
||||
return;
|
||||
LOGGER_WARNING("Failed to initialize call...");
|
||||
return -1;
|
||||
}
|
||||
|
||||
call->av_call = av_call;
|
||||
|
@ -547,55 +576,59 @@ void i_callback_invite(void* toxav_inst, MSICall* call)
|
|||
if (toxav->ccb.first)
|
||||
toxav->ccb.first(toxav, call->friend_id, call->peer_capabilities & msi_CapSAudio,
|
||||
call->peer_capabilities & msi_CapSVideo, toxav->ccb.second);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i_callback_ringing(void* toxav_inst, MSICall* call)
|
||||
{
|
||||
ToxAV* toxav = toxav_inst;
|
||||
if (toxav->scb.first)
|
||||
toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_RINGING, toxav->scb.second);
|
||||
}
|
||||
|
||||
void i_callback_start(void* toxav_inst, MSICall* call)
|
||||
int callback_start(void* toxav_inst, MSICall* call)
|
||||
{
|
||||
ToxAV* toxav = toxav_inst;
|
||||
|
||||
ToxAVCall* av_call = i_toxav_get_call(toxav, call->friend_id);
|
||||
ToxAVCall* av_call = call_get(toxav, call->friend_id);
|
||||
|
||||
if (av_call == NULL || !i_toxav_prepare_transmission(toxav, av_call)) {
|
||||
/* TODO send error */
|
||||
i_toxav_remove_call(toxav, call->friend_id);
|
||||
return;
|
||||
if (av_call == NULL || !call_prepare_transmission(av_call)) {
|
||||
call_remove(av_call);
|
||||
return -1;
|
||||
}
|
||||
|
||||
TOXAV_CALL_STATE state = capabilities_to_state(av_call->msi_call->peer_capabilities);
|
||||
|
||||
if (toxav->scb.first)
|
||||
toxav->scb.first(toxav, call->friend_id, state, toxav->scb.second);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i_callback_end(void* toxav_inst, MSICall* call)
|
||||
int callback_end(void* toxav_inst, MSICall* call)
|
||||
{
|
||||
ToxAV* toxav = toxav_inst;
|
||||
|
||||
i_toxav_kill_transmission(toxav, call->friend_id);
|
||||
i_toxav_remove_call(toxav, call->friend_id);
|
||||
call_kill_transmission(call->av_call);
|
||||
call_remove(call->av_call);
|
||||
|
||||
if (toxav->scb.first)
|
||||
toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_END, toxav->scb.second);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i_callback_error(void* toxav_inst, MSICall* call)
|
||||
int callback_error(void* toxav_inst, MSICall* call)
|
||||
{
|
||||
ToxAV* toxav = toxav_inst;
|
||||
if (toxav->scb.first)
|
||||
toxav->scb.first(toxav, call->friend_id, TOXAV_CALL_STATE_ERROR, toxav->scb.second);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i_callback_capabilites(void* toxav_inst, MSICall* call)
|
||||
int callback_capabilites(void* toxav_inst, MSICall* call)
|
||||
{
|
||||
ToxAV* toxav = toxav_inst;
|
||||
/* TODO handle this */
|
||||
if (toxav->scb.first)
|
||||
toxav->scb.first(toxav, call->friend_id,
|
||||
capabilities_to_state(call->peer_capabilities), toxav->scb.second);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities)
|
||||
|
@ -610,116 +643,21 @@ TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities)
|
|||
return TOXAV_CALL_STATE_PAUSED;
|
||||
}
|
||||
|
||||
ToxAVCall* i_toxav_get_call(ToxAV* av, uint32_t friend_number)
|
||||
bool audio_bitrate_invalid(uint32_t bitrate)
|
||||
{
|
||||
if (av->calls == NULL || av->calls_tail < friend_number)
|
||||
return NULL;
|
||||
|
||||
return av->calls[friend_number];
|
||||
/* Opus RFC 6716 section-2.1.1 dictates the following:
|
||||
* Opus supports all bitrates from 6 kbit/s to 510 kbit/s.
|
||||
*/
|
||||
return bitrate < 6 || bitrate > 510;
|
||||
}
|
||||
|
||||
ToxAVCall* i_toxav_add_call(ToxAV* av, uint32_t friend_number)
|
||||
bool video_bitrate_invalid(uint32_t bitrate)
|
||||
{
|
||||
ToxAVCall* rc = calloc(sizeof(ToxAVCall), 1);
|
||||
|
||||
if (rc == NULL)
|
||||
return NULL;
|
||||
|
||||
rc->friend_id = friend_number;
|
||||
|
||||
if (create_recursive_mutex(rc->mutex_control) != 0) {
|
||||
free(rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (create_recursive_mutex(rc->mutex_do) != 0) {
|
||||
pthread_mutex_destroy(rc->mutex_control);
|
||||
free(rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
if (av->calls == NULL) { /* Creating */
|
||||
av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1);
|
||||
|
||||
if (av->calls == NULL) {
|
||||
pthread_mutex_destroy(rc->mutex_control);
|
||||
pthread_mutex_destroy(rc->mutex_do);
|
||||
free(rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
av->calls_tail = av->calls_head = friend_number;
|
||||
|
||||
} else if (av->calls_tail < friend_number) { /* Appending */
|
||||
void* tmp = realloc(av->calls, sizeof(ToxAVCall*) * friend_number + 1);
|
||||
|
||||
if (tmp == NULL) {
|
||||
pthread_mutex_destroy(rc->mutex_control);
|
||||
pthread_mutex_destroy(rc->mutex_do);
|
||||
free(rc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
av->calls = tmp;
|
||||
|
||||
/* Set fields in between to null */
|
||||
int32_t i = av->calls_tail;
|
||||
for (; i < friend_number; i ++)
|
||||
av->calls[i] = NULL;
|
||||
|
||||
rc->prev = av->calls[av->calls_tail];
|
||||
av->calls[av->calls_tail]->next = rc;
|
||||
|
||||
av->calls_tail = friend_number;
|
||||
|
||||
} else if (av->calls_head > friend_number) { /* Inserting at front */
|
||||
rc->next = av->calls[av->calls_head];
|
||||
av->calls[av->calls_head]->prev = rc;
|
||||
av->calls_head = friend_number;
|
||||
}
|
||||
|
||||
av->calls[friend_number] = rc;
|
||||
return rc;
|
||||
/* TODO: If anyone knows the answer to this one please fill it up */
|
||||
return false;
|
||||
}
|
||||
|
||||
void i_toxav_remove_call(ToxAV* av, uint32_t friend_number)
|
||||
{
|
||||
ToxAVCall* tc = i_toxav_get_call(av, friend_number);
|
||||
|
||||
if (tc == NULL)
|
||||
return;
|
||||
|
||||
ToxAVCall* prev = tc->prev;
|
||||
ToxAVCall* next = tc->next;
|
||||
|
||||
pthread_mutex_destroy(tc->mutex_control);
|
||||
pthread_mutex_destroy(tc->mutex_do);
|
||||
|
||||
free(tc);
|
||||
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
else if (next)
|
||||
av->calls_head = next->friend_id;
|
||||
else goto CLEAR;
|
||||
|
||||
if (next)
|
||||
next->prev = prev;
|
||||
else if (prev)
|
||||
av->calls_tail = prev->friend_id;
|
||||
else goto CLEAR;
|
||||
|
||||
av->calls[friend_number] = NULL;
|
||||
return;
|
||||
|
||||
CLEAR:
|
||||
av->calls_head = av->calls_tail = 0;
|
||||
free(av->calls);
|
||||
av->calls = NULL;
|
||||
}
|
||||
|
||||
ToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
|
||||
ToxAVCall* call_new(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL* error)
|
||||
{
|
||||
TOXAV_ERR_CALL rc = TOXAV_ERR_CALL_OK;
|
||||
ToxAVCall* call = NULL;
|
||||
|
@ -734,39 +672,106 @@ ToxAVCall* i_toxav_init_call(ToxAV* av, uint32_t friend_number, TOXAV_ERR_CALL*
|
|||
goto END;
|
||||
}
|
||||
|
||||
if (i_toxav_get_call(av, friend_number) != NULL) {
|
||||
if (call_get(av, friend_number) != NULL) {
|
||||
rc = TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL;
|
||||
goto END;
|
||||
}
|
||||
|
||||
call = i_toxav_add_call(av, friend_number);
|
||||
|
||||
call = calloc(sizeof(ToxAVCall), 1);
|
||||
|
||||
if (call == NULL) {
|
||||
rc = TOXAV_ERR_CALL_MALLOC;
|
||||
goto END;
|
||||
}
|
||||
|
||||
END:
|
||||
call->av = av;
|
||||
call->friend_id = friend_number;
|
||||
|
||||
if (create_recursive_mutex(call->mutex_control) != 0) {
|
||||
free(call);
|
||||
call = NULL;
|
||||
rc = TOXAV_ERR_CALL_MALLOC;
|
||||
goto END;
|
||||
}
|
||||
|
||||
if (create_recursive_mutex(call->mutex_do) != 0) {
|
||||
pthread_mutex_destroy(call->mutex_control);
|
||||
free(call);
|
||||
call = NULL;
|
||||
rc = TOXAV_ERR_CALL_MALLOC;
|
||||
goto END;
|
||||
}
|
||||
|
||||
|
||||
if (av->calls == NULL) { /* Creating */
|
||||
av->calls = calloc (sizeof(ToxAVCall*), friend_number + 1);
|
||||
|
||||
if (av->calls == NULL) {
|
||||
pthread_mutex_destroy(call->mutex_control);
|
||||
pthread_mutex_destroy(call->mutex_do);
|
||||
free(call);
|
||||
call = NULL;
|
||||
rc = TOXAV_ERR_CALL_MALLOC;
|
||||
goto END;
|
||||
}
|
||||
|
||||
av->calls_tail = av->calls_head = friend_number;
|
||||
|
||||
} else if (av->calls_tail < friend_number) { /* Appending */
|
||||
void* tmp = realloc(av->calls, sizeof(ToxAVCall*) * friend_number + 1);
|
||||
|
||||
if (tmp == NULL) {
|
||||
pthread_mutex_destroy(call->mutex_control);
|
||||
pthread_mutex_destroy(call->mutex_do);
|
||||
free(call);
|
||||
call = NULL;
|
||||
rc = TOXAV_ERR_CALL_MALLOC;
|
||||
goto END;
|
||||
}
|
||||
|
||||
av->calls = tmp;
|
||||
|
||||
/* Set fields in between to null */
|
||||
int32_t i = av->calls_tail;
|
||||
for (; i < friend_number; i ++)
|
||||
av->calls[i] = NULL;
|
||||
|
||||
call->prev = av->calls[av->calls_tail];
|
||||
av->calls[av->calls_tail]->next = call;
|
||||
|
||||
av->calls_tail = friend_number;
|
||||
|
||||
} else if (av->calls_head > friend_number) { /* Inserting at front */
|
||||
call->next = av->calls[av->calls_head];
|
||||
av->calls[av->calls_head]->prev = call;
|
||||
av->calls_head = friend_number;
|
||||
}
|
||||
|
||||
av->calls[friend_number] = call;
|
||||
|
||||
END:
|
||||
if (error)
|
||||
*error = rc;
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
bool i_toxav_audio_bitrate_invalid(uint32_t bitrate)
|
||||
ToxAVCall* call_get(ToxAV* av, uint32_t friend_number)
|
||||
{
|
||||
/* Opus RFC 6716 section-2.1.1 dictates the following:
|
||||
* Opus supports all bitrates from 6 kbit/s to 510 kbit/s.
|
||||
*/
|
||||
return bitrate < 6 || bitrate > 510;
|
||||
if (av->calls == NULL || av->calls_tail < friend_number)
|
||||
return NULL;
|
||||
|
||||
return av->calls[friend_number];
|
||||
}
|
||||
|
||||
bool i_toxav_video_bitrate_invalid(uint32_t bitrate)
|
||||
{
|
||||
/* TODO: If anyone knows the answer to this one please fill it up */
|
||||
return false;
|
||||
}
|
||||
|
||||
bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call)
|
||||
bool call_prepare_transmission(ToxAVCall* call)
|
||||
{
|
||||
if (call == NULL)
|
||||
return false;
|
||||
|
||||
ToxAV* av = call->av;
|
||||
|
||||
if (!av->acb.first && !av->vcb.first)
|
||||
/* It makes no sense to have CSession without callbacks */
|
||||
return false;
|
||||
|
@ -830,7 +835,7 @@ bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call)
|
|||
goto FAILURE;
|
||||
}
|
||||
|
||||
rtp_register_for_receiving(call->rtps[audio_index]);
|
||||
rtp_start_receiving(call->rtps[audio_index]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -854,8 +859,8 @@ bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call)
|
|||
LOGGER_WARNING("Failed to enable video receiving!");
|
||||
goto FAILURE;
|
||||
}
|
||||
|
||||
rtp_register_for_receiving(call->rtps[audio_index]);
|
||||
|
||||
rtp_start_receiving(call->rtps[audio_index]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -863,7 +868,7 @@ bool i_toxav_prepare_transmission(ToxAV* av, ToxAVCall* call)
|
|||
pthread_mutex_unlock(call->mutex_control);
|
||||
return true;
|
||||
|
||||
FAILURE:
|
||||
FAILURE:
|
||||
rtp_kill(call->rtps[audio_index]);
|
||||
call->rtps[audio_index] = NULL;
|
||||
rtp_kill(call->rtps[video_index]);
|
||||
|
@ -878,25 +883,19 @@ FAILURE:
|
|||
pthread_mutex_unlock(call->mutex_control);
|
||||
return false;
|
||||
|
||||
MUTEX_INIT_ERROR:
|
||||
MUTEX_INIT_ERROR:
|
||||
pthread_mutex_unlock(call->mutex_control);
|
||||
LOGGER_ERROR("Mutex initialization failed!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number)
|
||||
void call_kill_transmission(ToxAVCall* call)
|
||||
{
|
||||
ToxAVCall* call = i_toxav_get_call(av, friend_number);
|
||||
if (!call)
|
||||
if (call == NULL || call->active == 0)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(call->mutex_control);
|
||||
|
||||
if (!call->active) {
|
||||
pthread_mutex_unlock(call->mutex_control);
|
||||
return;
|
||||
}
|
||||
|
||||
call->active = 0;
|
||||
|
||||
pthread_mutex_lock(call->mutex_encoding_audio);
|
||||
|
@ -918,4 +917,41 @@ void i_toxav_kill_transmission(ToxAV* av, uint32_t friend_number)
|
|||
pthread_mutex_destroy(call->mutex_do);
|
||||
|
||||
pthread_mutex_unlock(call->mutex_control);
|
||||
}
|
||||
|
||||
void call_remove(ToxAVCall* call)
|
||||
{
|
||||
if (call == NULL)
|
||||
return;
|
||||
|
||||
uint32_t friend_id = call->friend_id;
|
||||
ToxAV* av = call->av;
|
||||
|
||||
ToxAVCall* prev = call->prev;
|
||||
ToxAVCall* next = call->next;
|
||||
|
||||
pthread_mutex_destroy(call->mutex_control);
|
||||
pthread_mutex_destroy(call->mutex_do);
|
||||
|
||||
free(call);
|
||||
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
else if (next)
|
||||
av->calls_head = next->friend_id;
|
||||
else goto CLEAR;
|
||||
|
||||
if (next)
|
||||
next->prev = prev;
|
||||
else if (prev)
|
||||
av->calls_tail = prev->friend_id;
|
||||
else goto CLEAR;
|
||||
|
||||
av->calls[friend_id] = NULL;
|
||||
return;
|
||||
|
||||
CLEAR:
|
||||
av->calls_head = av->calls_tail = 0;
|
||||
free(av->calls);
|
||||
av->calls = NULL;
|
||||
}
|
|
@ -186,11 +186,6 @@ bool toxav_answer(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, ui
|
|||
*
|
||||
******************************************************************************/
|
||||
typedef enum TOXAV_CALL_STATE {
|
||||
/**
|
||||
* The friend's client is aware of the call. This happens after calling
|
||||
* toxav_call and the initial call request has been received.
|
||||
*/
|
||||
TOXAV_CALL_STATE_RINGING,
|
||||
/**
|
||||
* Not sending anything. Either the friend requested that this client stops
|
||||
* sending anything, or the client turned off both audio and video by setting
|
||||
|
|
Loading…
Reference in New Issue
Block a user