Implement pausing

This commit is contained in:
mannol 2015-02-22 23:41:40 +01:00
parent 29601feb76
commit 9e65cd5337
4 changed files with 266 additions and 115 deletions

View File

@ -16,13 +16,25 @@
typedef struct {
bool incoming;
bool ringing;
bool ended;
bool errored;
bool sending;
bool paused;
TOXAV_CALL_STATE state;
} CallControl;
const char* stringify_state(TOXAV_CALL_STATE s)
{
static const char* strings[] =
{
"NOT SENDING",
"SENDING AUDIO",
"SENDING VIDEO",
"SENDING AUDIO AND VIDEO",
"PAUSED",
"END",
"ERROR"
};
return strings [s];
};
/**
* Callbacks
@ -34,53 +46,9 @@ void t_toxav_call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool
}
void t_toxav_call_state_cb(ToxAV *av, uint32_t friend_number, TOXAV_CALL_STATE state, void *user_data)
{
printf("Handling CALL STATE callback: ");
printf("Handling CALL STATE callback: %s\n", stringify_state(state));
if (((CallControl*)user_data)->paused)
((CallControl*)user_data)->paused = false;
switch (state)
{
case TOXAV_CALL_STATE_NOT_SENDING: {
printf("Not sending");
((CallControl*)user_data)->sending = false;
} break;
case TOXAV_CALL_STATE_SENDING_A: {
printf("Sending Audio");
((CallControl*)user_data)->sending = true;
} break;
case TOXAV_CALL_STATE_SENDING_V: {
printf("Sending Video");
((CallControl*)user_data)->sending = true;
} break;
case TOXAV_CALL_STATE_SENDING_AV: {
printf("Sending Both");
((CallControl*)user_data)->sending = true;
} break;
case TOXAV_CALL_STATE_PAUSED: {
printf("Paused");
((CallControl*)user_data)->paused = true;
((CallControl*)user_data)->sending = false;
} break;
case TOXAV_CALL_STATE_END: {
printf("Ended");
((CallControl*)user_data)->ended = true;
((CallControl*)user_data)->sending = false;
} break;
case TOXAV_CALL_STATE_ERROR: {
printf("Error");
((CallControl*)user_data)->errored = true;
((CallControl*)user_data)->sending = false;
} break;
}
printf("\n");
((CallControl*)user_data)->state = state;
}
void t_toxav_receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height,
@ -211,27 +179,24 @@ int main (int argc, char** argv)
long long unsigned int start_time = time(NULL); \
\
\
while (!AliceCC.ended || !BobCC.ended) { \
while (BobCC.state != TOXAV_CALL_STATE_END) { \
\
if (BobCC.incoming) { \
TOXAV_ERR_ANSWER rc; \
toxav_answer(BobAV, 0, 48, 4000, &rc); \
toxav_answer(BobAV, 0, A_BR, V_BR, &rc); \
\
if (rc != TOXAV_ERR_ANSWER_OK) { \
printf("toxav_answer failed: %d\n", rc); \
exit(1); \
} \
BobCC.incoming = false; \
BobCC.sending = true; /* There is no more start callback when answering */\
} \
else if (AliceCC.sending && BobCC.sending) { \
} else { \
/* TODO rtp */ \
\
if (time(NULL) - start_time == 5) { \
\
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); \
@ -286,7 +251,7 @@ int main (int argc, char** argv)
}
}
while (!AliceCC.ended)
while (AliceCC.state != TOXAV_CALL_STATE_END)
iterate(Bsn, AliceAV, BobAV);
printf("Success!\n");
@ -323,12 +288,102 @@ int main (int argc, char** argv)
}
/* Alice will not receive end state */
while (!BobCC.ended)
while (BobCC.state != TOXAV_CALL_STATE_END)
iterate(Bsn, AliceAV, BobAV);
printf("Success!\n");
}
{ /* Check Mute-Unmute etc */
printf("\nTrying mute functionality...\n");
memset(&AliceCC, 0, sizeof(CallControl));
memset(&BobCC, 0, sizeof(CallControl));
/* Assume sending audio and video */
{
TOXAV_ERR_CALL rc;
toxav_call(AliceAV, 0, 48, 1000, &rc);
if (rc != TOXAV_ERR_CALL_OK) {
printf("toxav_call failed: %d\n", rc);
exit(1);
}
}
while (!BobCC.incoming)
iterate(Bsn, AliceAV, BobAV);
/* At first try all stuff while in invalid state */
assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL));
assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL));
assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_VIDEO, NULL));
assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL));
assert(!toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_VIDEO, NULL));
{
TOXAV_ERR_ANSWER rc;
toxav_answer(BobAV, 0, 48, 4000, &rc);
if (rc != TOXAV_ERR_ANSWER_OK) {
printf("toxav_answer failed: %d\n", rc);
exit(1);
}
}
iterate(Bsn, AliceAV, BobAV);
/* Pause and Resume */
printf("Pause and Resume\n");
assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_PAUSE, NULL));
iterate(Bsn, AliceAV, BobAV);
assert(BobCC.state == TOXAV_CALL_STATE_PAUSED);
assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_RESUME, NULL));
iterate(Bsn, AliceAV, BobAV);
assert(BobCC.state == TOXAV_CALL_STATE_SENDING_AV);
/* Mute/Unmute single */
printf("Mute/Unmute single\n");
assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL));
iterate(Bsn, AliceAV, BobAV);
assert(BobCC.state == TOXAV_CALL_CONTROL_MUTE_AUDIO);
assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL));
iterate(Bsn, AliceAV, BobAV);
assert(BobCC.state == TOXAV_CALL_CONTROL_UNMUTE_AUDIO);
/* Mute/Unmute both */
printf("Mute/Unmute both\n");
assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_AUDIO, NULL));
iterate(Bsn, AliceAV, BobAV);
assert(BobCC.state == TOXAV_CALL_STATE_SENDING_V);
assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_MUTE_VIDEO, NULL));
iterate(Bsn, AliceAV, BobAV);
assert(BobCC.state == TOXAV_CALL_STATE_NOT_SENDING);
assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_AUDIO, NULL));
iterate(Bsn, AliceAV, BobAV);
assert(BobCC.state == TOXAV_CALL_STATE_SENDING_A);
assert(toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_UNMUTE_VIDEO, NULL));
iterate(Bsn, AliceAV, BobAV);
assert(BobCC.state == TOXAV_CALL_STATE_SENDING_AV);
{
TOXAV_ERR_CALL_CONTROL rc;
toxav_call_control(AliceAV, 0, TOXAV_CALL_CONTROL_CANCEL, &rc);
if (rc != TOXAV_ERR_CALL_CONTROL_OK) {
printf("toxav_call_control failed: %d\n", rc);
exit(1);
}
}
iterate(Bsn, AliceAV, BobAV);
assert(BobCC.state == TOXAV_CALL_STATE_END);
printf("Success!\n");
}
printf("\nTest successful!\n");
return 0;
}

View File

@ -41,8 +41,8 @@ typedef enum {
msi_InvalidState,
msi_StrayMessage,
msi_SystemError,
msi_ErrUndisclosed,
msi_HandleError,
msi_ErrUndisclosed, /* NOTE: must be last enum otherwise parsing wont work */
} MSIError;
/**

View File

@ -235,6 +235,8 @@ bool toxav_call(ToxAV* av, uint32_t friend_number, uint32_t audio_bit_rate, uint
return false;
}
call->msi_call->av_call = call;
return true;
}
@ -310,12 +312,22 @@ 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 &&
if (!call->active) {
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
/* Only act if paused and had media transfer active before */
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;
if (msi_change_capabilities(call->msi_call,
call->last_capabilities) == -1) {
/* The only reason for this function to fail is invalid state
* ( not active ) */
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
rtp_start_receiving(call->rtps[audio_index]);
rtp_start_receiving(call->rtps[video_index]);
@ -323,13 +335,21 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
} break;
case TOXAV_CALL_CONTROL_PAUSE: {
if (!call->active) {
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
/* Only act if not already paused */
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;
if (msi_change_capabilities(call->msi_call, 0) == -1 ) {
/* The only reason for this function to fail is invalid state
* ( not active ) */
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
rtp_stop_receiving(call->rtps[audio_index]);
rtp_stop_receiving(call->rtps[video_index]);
@ -341,26 +361,84 @@ bool toxav_call_control(ToxAV* av, uint32_t friend_number, TOXAV_CALL_CONTROL co
msi_hangup(call->msi_call);
/* No mather the case, terminate the call */
call_kill_transmission(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;
if (!call->active) {
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
if (call->msi_call->self_capabilities & msi_CapRAudio) {
if (msi_change_capabilities(call->msi_call, call->
msi_call->self_capabilities ^ msi_CapRAudio) == -1) {
/* The only reason for this function to fail is invalid state
* ( not active ) */
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
rtp_stop_receiving(call->rtps[audio_index]);
}
} break;
case TOXAV_CALL_CONTROL_MUTE_VIDEO: {
if (!call->active) {
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
if (call->msi_call->self_capabilities & msi_CapRVideo) {
if (msi_change_capabilities(call->msi_call, call->
msi_call->self_capabilities ^ msi_CapRVideo) == -1) {
/* The only reason for this function to fail is invalid state
* ( not active ) */
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
rtp_stop_receiving(call->rtps[video_index]);
}
} break;
case TOXAV_CALL_CONTROL_UNMUTE_AUDIO: {
if (!call->active) {
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
if (call->msi_call->self_capabilities & ~msi_CapRAudio) {
if (msi_change_capabilities(call->msi_call, call->
msi_call->self_capabilities | msi_CapRAudio) == -1) {
/* The only reason for this function to fail is invalid state
* ( not active ) */
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
rtp_start_receiving(call->rtps[audio_index]);
}
} break;
case TOXAV_CALL_CONTROL_UNMUTE_VIDEO: {
if (!call->active) {
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
if (call->msi_call->self_capabilities & ~msi_CapRVideo) {
if (msi_change_capabilities(call->msi_call, call->
msi_call->self_capabilities | msi_CapRVideo) == -1) {
/* The only reason for this function to fail is invalid state
* ( not active ) */
rc = TOXAV_ERR_CALL_CONTROL_FRIEND_NOT_IN_CALL;
goto END;
}
rtp_start_receiving(call->rtps[video_index]);
}
} break;
}
@ -558,8 +636,6 @@ void toxav_callback_receive_audio_frame(ToxAV* av, toxav_receive_audio_frame_cb*
* :: Internal
*
******************************************************************************/
/** TODO:
*/
int callback_invite(void* toxav_inst, MSICall* call)
{
ToxAV* toxav = toxav_inst;
@ -586,15 +662,20 @@ int callback_start(void* toxav_inst, MSICall* call)
ToxAVCall* av_call = call_get(toxav, call->friend_id);
if (av_call == NULL || !call_prepare_transmission(av_call)) {
if (av_call == NULL) {
/* Should this ever happen? */
return -1;
}
if (!call_prepare_transmission(av_call)) {
callback_error(toxav_inst, 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);
toxav->scb.first(toxav, call->friend_id,
capabilities_to_state(call->peer_capabilities), toxav->scb.second);
return 0;
}
@ -603,18 +684,19 @@ int callback_end(void* toxav_inst, MSICall* call)
{
ToxAV* toxav = toxav_inst;
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);
call_kill_transmission(call->av_call);
call_remove(call->av_call);
return 0;
}
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);
@ -633,14 +715,16 @@ int callback_capabilites(void* toxav_inst, MSICall* call)
TOXAV_CALL_STATE capabilities_to_state(uint8_t capabilities)
{
if ((capabilities & msi_CapSAudio) && (capabilities & msi_CapSVideo))
if (capabilities == 0)
return TOXAV_CALL_STATE_PAUSED;
else if ((capabilities & msi_CapSAudio) && (capabilities & msi_CapSVideo))
return TOXAV_CALL_STATE_SENDING_AV;
else if (capabilities & msi_CapSAudio)
return TOXAV_CALL_STATE_SENDING_A;
else if (capabilities & msi_CapSVideo)
return TOXAV_CALL_STATE_SENDING_V;
else
return TOXAV_CALL_STATE_PAUSED;
return TOXAV_CALL_STATE_NOT_SENDING;
}
bool audio_bitrate_invalid(uint32_t bitrate)
@ -798,8 +882,6 @@ bool call_prepare_transmission(ToxAVCall* call)
goto MUTEX_INIT_ERROR;
}
uint8_t capabilities = call->msi_call->self_capabilities;
call->cs = cs_new(call->msi_call->peer_vfpsz);
if ( !call->cs ) {
@ -813,8 +895,7 @@ bool call_prepare_transmission(ToxAVCall* call)
memcpy(&call->cs->acb, &av->acb, sizeof(av->acb));
memcpy(&call->cs->vcb, &av->vcb, sizeof(av->vcb));
if (capabilities & msi_CapSAudio || capabilities & msi_CapRAudio) { /* Prepare audio sending */
{ /* Prepare audio */
call->rtps[audio_index] = rtp_new(rtp_TypeAudio, av->m, call->friend_id);
if ( !call->rtps[audio_index] ) {
@ -824,22 +905,21 @@ bool call_prepare_transmission(ToxAVCall* call)
call->rtps[audio_index]->cs = call->cs;
if (cs_enable_audio_sending(call->cs, call->s_audio_b, 2) != 0) {
/* Only enable sending if bitrate is defined */
if (call->s_audio_b > 0 && cs_enable_audio_sending(call->cs, call->s_audio_b, 2) != 0) {
LOGGER_WARNING("Failed to enable audio sending!");
goto FAILURE;
}
if (capabilities & msi_CapRAudio) {
if (cs_enable_audio_receiving(call->cs) != 0) {
LOGGER_WARNING("Failed to enable audio receiving!");
goto FAILURE;
}
rtp_start_receiving(call->rtps[audio_index]);
if (cs_enable_audio_receiving(call->cs) != 0) {
LOGGER_WARNING("Failed to enable audio receiving!");
goto FAILURE;
}
rtp_start_receiving(call->rtps[audio_index]);
}
if (capabilities & msi_CapSVideo || capabilities & msi_CapRVideo) { /* Prepare video rtp */
{ /* Prepare video */
call->rtps[video_index] = rtp_new(rtp_TypeVideo, av->m, call->friend_id);
if ( !call->rtps[video_index] ) {
@ -849,26 +929,26 @@ bool call_prepare_transmission(ToxAVCall* call)
call->rtps[video_index]->cs = call->cs;
if (cs_enable_video_sending(call->cs, call->s_video_b) != 0) {
/* Only enable sending if bitrate is defined */
if (call->s_video_b > 0 && cs_enable_video_sending(call->cs, call->s_video_b) != 0) {
LOGGER_WARNING("Failed to enable video sending!");
goto FAILURE;
}
if (capabilities & msi_CapRVideo) {
if (cs_enable_video_receiving(call->cs) != 0) {
LOGGER_WARNING("Failed to enable video receiving!");
goto FAILURE;
}
rtp_start_receiving(call->rtps[audio_index]);
if (cs_enable_video_receiving(call->cs) != 0) {
LOGGER_WARNING("Failed to enable video receiving!");
goto FAILURE;
}
rtp_start_receiving(call->rtps[audio_index]);
}
call->active = 1;
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]);
@ -883,7 +963,7 @@ bool call_prepare_transmission(ToxAVCall* call)
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;

View File

@ -246,7 +246,7 @@ void toxav_callback_call_state(ToxAV *av, toxav_call_state_cb *function, void *u
typedef enum TOXAV_CALL_CONTROL {
/**
* Resume a previously paused call. Only valid if the pause was caused by this
* client. Not valid before the call is accepted.
* client, if not, this control is ignored. Not valid before the call is accepted.
*/
TOXAV_CALL_CONTROL_RESUME,
/**
@ -269,7 +269,19 @@ typedef enum TOXAV_CALL_CONTROL {
* compliance, this will cause the `receive_video_frame` event to stop being
* triggered on receiving an video frame from the friend.
*/
TOXAV_CALL_CONTROL_MUTE_VIDEO
TOXAV_CALL_CONTROL_MUTE_VIDEO,
/**
* Notify the friend that we are AGAIN ready to handle incoming audio.
* This control will not work if the call is not started with audio
* initiated.
*/
TOXAV_CALL_CONTROL_UNMUTE_AUDIO,
/**
* Notify the friend that we are AGAIN ready to handle incoming video.
* This control will not work if the call is not started with video
* initiated.
*/
TOXAV_CALL_CONTROL_UNMUTE_VIDEO
} TOXAV_CALL_CONTROL;
typedef enum TOXAV_ERR_CALL_CONTROL {
TOXAV_ERR_CALL_CONTROL_OK,
@ -296,7 +308,11 @@ typedef enum TOXAV_ERR_CALL_CONTROL {
* other party paused the call. The call will resume after both parties sent
* the RESUME control.
*/
TOXAV_ERR_CALL_CONTROL_ALREADY_PAUSED
TOXAV_ERR_CALL_CONTROL_ALREADY_PAUSED,
/**
* Tried to unmute a call that was not already muted.
*/
TOXAV_ERR_CALL_CONTROL_NOT_MUTED
} TOXAV_ERR_CALL_CONTROL;
/**
* Sends a call control command to a friend.