2012-04-18 21:44:51 +00:00
|
|
|
/*
|
|
|
|
Serval Voice Over Mesh Protocol (VoMP)
|
2012-08-02 01:59:37 +00:00
|
|
|
Copyright (C) 2012 Paul Gardner-Stephen
|
|
|
|
Copyright (C) 2012 Serval Project Inc.
|
2012-04-18 21:44:51 +00:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU General Public License
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2012-04-19 19:49:42 +00:00
|
|
|
VoMP works using a 6-state model of a phone call, and relies on MDP for
|
2012-04-18 21:44:51 +00:00
|
|
|
auth-cryption of frames. VoMP provides it's own replay protection.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "serval.h"
|
2012-08-02 01:59:37 +00:00
|
|
|
#include "strbuf.h"
|
2012-04-18 21:44:51 +00:00
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
Typical call state lifecycle between 2 parties.
|
|
|
|
Legend;
|
|
|
|
# incoming command from monitor client
|
|
|
|
$ outgoing monitor status
|
|
|
|
<> vomp packet with state change sent across the network
|
2012-08-08 01:26:05 +00:00
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
Monitor Init
|
2012-08-08 01:26:05 +00:00
|
|
|
# MONITOR VOMP [supported codec list]
|
2012-08-06 05:16:46 +00:00
|
|
|
|
|
|
|
Dialing
|
2012-08-08 01:26:05 +00:00
|
|
|
// client requests an outgoing call
|
|
|
|
# CALL [sid] [myDid] [TheirDid]
|
2012-08-06 05:16:46 +00:00
|
|
|
> CALLPREP + codecs
|
2012-08-08 01:26:05 +00:00
|
|
|
// let the client know what token we are going to use for the remainder of the call
|
|
|
|
$ CALLTO [token] [mySid] [myDid] [TheirSid] [TheirDid]
|
|
|
|
// allocate a session number and tell them our codecs,
|
|
|
|
// but we don't need to do anything else yet,
|
|
|
|
// this might be a replay attack
|
2012-08-06 05:16:46 +00:00
|
|
|
< NOCALL + codecs
|
2012-08-08 01:26:05 +00:00
|
|
|
// Ok, we have a network path, lets try to establish the call
|
2012-08-06 05:16:46 +00:00
|
|
|
> RINGOUT
|
2012-08-08 01:26:05 +00:00
|
|
|
// (Note that if both parties are trying to dial each other,
|
|
|
|
// the call should jump straight to INCALL)
|
|
|
|
// inform client about the call request
|
|
|
|
$ CALLFROM [token] [mySid] [myDid] [TheirSid] [TheirDid]
|
|
|
|
// Note that we may need to wait for other external processes
|
|
|
|
// before a phone is actually ringing
|
2012-08-06 05:16:46 +00:00
|
|
|
# RING [token]
|
2012-08-08 01:26:05 +00:00
|
|
|
< RINGIN
|
|
|
|
// All good, there's a phone out there ringing, you can indicate that to the user
|
2012-08-06 05:16:46 +00:00
|
|
|
$ RINGING [token]
|
|
|
|
|
|
|
|
Answering
|
|
|
|
# PICKUP [token]
|
|
|
|
< INCALL
|
2012-08-08 01:26:05 +00:00
|
|
|
// The client can now start sending audio
|
2012-08-06 05:16:46 +00:00
|
|
|
> INCALL
|
|
|
|
$ INCALL [token]
|
2012-08-08 01:26:05 +00:00
|
|
|
// The client can now start sending audio
|
2012-08-06 05:16:46 +00:00
|
|
|
$ INCALL [token]
|
|
|
|
|
|
|
|
Tell any clients that the call hasn't timed out yet
|
|
|
|
(if servald is behaving this should be redundant, if it isn't behaving how do we hangup?)
|
|
|
|
$ KEEPALIVE [token]
|
|
|
|
|
|
|
|
Hanging up (may also be triggered on network or call establishment timeout)
|
|
|
|
# HANGUP [token]
|
|
|
|
> CALLENDED
|
|
|
|
$ HANGUP [token]
|
|
|
|
< CALLENDED
|
|
|
|
$ HANGUP [token]
|
|
|
|
*/
|
|
|
|
|
2012-08-08 01:26:05 +00:00
|
|
|
// ideally these id's should only be used on the network, with monitor events to inform clients of state changes
|
|
|
|
#define VOMP_STATE_NOCALL 1
|
|
|
|
#define VOMP_STATE_CALLPREP 2
|
|
|
|
#define VOMP_STATE_RINGINGOUT 3
|
|
|
|
#define VOMP_STATE_RINGINGIN 4
|
|
|
|
#define VOMP_STATE_INCALL 5
|
|
|
|
#define VOMP_STATE_CALLENDED 6
|
2012-08-06 05:16:46 +00:00
|
|
|
|
|
|
|
struct vomp_call_state {
|
|
|
|
struct sched_ent alarm;
|
|
|
|
vomp_call_half local;
|
|
|
|
vomp_call_half remote;
|
|
|
|
int initiated_call;
|
|
|
|
int fast_audio;
|
|
|
|
time_ms_t create_time;
|
|
|
|
time_ms_t last_activity;
|
|
|
|
time_ms_t audio_clock;
|
|
|
|
int audio_started;
|
|
|
|
// last local & remote status we sent to all interested parties
|
|
|
|
int last_sent_status;
|
|
|
|
unsigned char remote_codec_list[256];
|
|
|
|
int recent_sample_rotor;
|
|
|
|
vomp_sample_block recent_samples[VOMP_MAX_RECENT_SAMPLES];
|
|
|
|
|
|
|
|
int sample_pos;
|
|
|
|
unsigned int seen_samples[VOMP_MAX_RECENT_SAMPLES *4];
|
|
|
|
};
|
|
|
|
|
2012-04-18 21:44:51 +00:00
|
|
|
/* Although we only support one call at a time, we allow for multiple call states.
|
|
|
|
This is partly to deal with denial of service attacks that might occur by causing
|
|
|
|
the ejection of newly allocated session numbers before the caller has had a chance
|
|
|
|
to progress the call to a further state. */
|
|
|
|
int vomp_call_count=0;
|
2012-04-19 19:49:42 +00:00
|
|
|
int vomp_active_call=-1;
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state vomp_call_states[VOMP_MAX_CALLS];
|
2012-07-03 05:43:39 +00:00
|
|
|
struct profile_total vomp_stats;
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
static void vomp_process_tick(struct sched_ent *alarm);
|
|
|
|
static int dump_vomp_status();
|
|
|
|
static const char *vomp_describe_codec(int c);
|
|
|
|
strbuf strbuf_append_vomp_supported_codecs(strbuf sb, const unsigned char supported_codecs[256]);
|
2012-04-18 21:44:51 +00:00
|
|
|
|
2012-04-23 04:00:22 +00:00
|
|
|
/* which codecs we support (set by registered listener) */
|
|
|
|
unsigned char vomp_local_codec_list[256];
|
|
|
|
|
2012-04-19 12:23:40 +00:00
|
|
|
/* Now keep track of who wants to know what we are up to */
|
|
|
|
int vomp_interested_usock_count=0;
|
|
|
|
#define VOMP_MAX_INTERESTED 128
|
|
|
|
struct sockaddr_un *vomp_interested_usocks[VOMP_MAX_INTERESTED];
|
|
|
|
int vomp_interested_usock_lengths[VOMP_MAX_INTERESTED];
|
2012-08-09 02:44:32 +00:00
|
|
|
time_ms_t vomp_interested_expiries[VOMP_MAX_INTERESTED];
|
2012-04-19 12:23:40 +00:00
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *vomp_find_call_by_session(int session_token)
|
2012-04-19 19:49:42 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i=0;i<vomp_call_count;i++)
|
|
|
|
if (session_token==vomp_call_states[i].local.session)
|
|
|
|
return &vomp_call_states[i];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
int vomp_generate_session_id()
|
|
|
|
{
|
2012-07-03 05:43:39 +00:00
|
|
|
int session_id=0;
|
|
|
|
while (!session_id)
|
|
|
|
{
|
|
|
|
if (urandombytes((unsigned char *)&session_id,sizeof(int)))
|
|
|
|
return WHY("Insufficient entropy");
|
|
|
|
session_id&=VOMP_SESSION_MASK;
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP) DEBUGF("session=0x%08x",session_id);
|
2012-07-03 05:43:39 +00:00
|
|
|
int i;
|
|
|
|
/* reject duplicate call session numbers */
|
|
|
|
for(i=0;i<vomp_call_count;i++)
|
|
|
|
if (session_id==vomp_call_states[i].local.session
|
|
|
|
||session_id==vomp_call_states[i].local.session){
|
|
|
|
session_id=0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return session_id;
|
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *vomp_create_call(unsigned char *remote_sid,
|
2012-07-03 05:43:39 +00:00
|
|
|
unsigned char *local_sid,
|
|
|
|
unsigned int remote_session,
|
2012-08-08 01:26:05 +00:00
|
|
|
unsigned int local_session)
|
2012-08-02 01:59:37 +00:00
|
|
|
{
|
2012-07-05 06:22:21 +00:00
|
|
|
int i;
|
2012-07-03 05:43:39 +00:00
|
|
|
if (!local_session)
|
|
|
|
local_session=vomp_generate_session_id();
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *call = &vomp_call_states[vomp_call_count];
|
2012-07-03 05:43:39 +00:00
|
|
|
vomp_call_count++;
|
|
|
|
|
|
|
|
/* prepare slot */
|
2012-08-06 05:16:46 +00:00
|
|
|
bzero(call,sizeof(struct vomp_call_state));
|
2012-07-03 05:43:39 +00:00
|
|
|
bcopy(local_sid,call->local.sid,SID_SIZE);
|
|
|
|
bcopy(remote_sid,call->remote.sid,SID_SIZE);
|
|
|
|
call->local.session=local_session;
|
|
|
|
call->remote.session=remote_session;
|
2012-08-08 01:26:05 +00:00
|
|
|
call->local.state=VOMP_STATE_NOCALL;
|
|
|
|
call->remote.state=VOMP_STATE_NOCALL;
|
2012-07-05 06:22:21 +00:00
|
|
|
call->last_sent_status=-1;
|
2012-07-30 07:52:38 +00:00
|
|
|
call->create_time=gettime_ms();
|
2012-07-03 05:43:39 +00:00
|
|
|
call->last_activity=call->create_time;
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
// fill sample cache with invalid times
|
|
|
|
for (i=0;i<VOMP_MAX_RECENT_SAMPLES *4;i++)
|
|
|
|
call->seen_samples[i]=0xFFFFFFFF;
|
|
|
|
|
2012-07-03 05:43:39 +00:00
|
|
|
call->alarm.alarm = call->create_time+VOMP_CALL_STATUS_INTERVAL;
|
|
|
|
call->alarm.function = vomp_process_tick;
|
|
|
|
vomp_stats.name="vomp_process_tick";
|
|
|
|
call->alarm.stats=&vomp_stats;
|
|
|
|
schedule(&call->alarm);
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("Returning new call #%d",local_session);
|
2012-07-03 05:43:39 +00:00
|
|
|
return call;
|
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *vomp_find_or_create_call(unsigned char *remote_sid,
|
2012-04-18 21:44:51 +00:00
|
|
|
unsigned char *local_sid,
|
|
|
|
unsigned int sender_session,
|
2012-04-22 09:45:55 +00:00
|
|
|
unsigned int recvr_session,
|
2012-07-03 05:43:39 +00:00
|
|
|
int sender_state,
|
|
|
|
int recvr_state)
|
2012-04-18 21:44:51 +00:00
|
|
|
{
|
|
|
|
int i;
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *call;
|
2012-07-03 05:43:39 +00:00
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("%d calls already in progress.",vomp_call_count);
|
2012-04-18 21:44:51 +00:00
|
|
|
for(i=0;i<vomp_call_count;i++)
|
|
|
|
{
|
2012-07-03 05:43:39 +00:00
|
|
|
call = &vomp_call_states[i];
|
|
|
|
|
2012-04-18 21:44:51 +00:00
|
|
|
/* do the fast comparison first, and only if that matches proceed to
|
|
|
|
the slower SID comparisons */
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("asking for %06x:%06x, this call %06x:%06x",
|
2012-04-24 05:10:30 +00:00
|
|
|
sender_session,recvr_session,
|
2012-07-03 05:43:39 +00:00
|
|
|
call->remote.session,
|
|
|
|
call->local.session);
|
2012-04-21 21:38:33 +00:00
|
|
|
|
|
|
|
int checked=0;
|
2012-07-03 05:43:39 +00:00
|
|
|
if (call->remote.session&&sender_session) {
|
2012-04-21 21:38:33 +00:00
|
|
|
checked++;
|
2012-07-03 05:43:39 +00:00
|
|
|
if(sender_session!=call->remote.session)
|
2012-04-21 21:38:33 +00:00
|
|
|
continue;
|
|
|
|
}
|
2012-07-03 05:43:39 +00:00
|
|
|
if (call->local.session&&recvr_session) {
|
2012-04-21 21:38:33 +00:00
|
|
|
checked++;
|
2012-07-03 05:43:39 +00:00
|
|
|
if(recvr_session!=call->local.session)
|
2012-04-21 21:38:33 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!checked) continue;
|
2012-07-03 05:43:39 +00:00
|
|
|
if (memcmp(remote_sid,call->remote.sid,SID_SIZE)) continue;
|
|
|
|
if (memcmp(local_sid,call->local.sid,SID_SIZE)) continue;
|
|
|
|
|
|
|
|
/* it matches. */
|
2012-04-18 21:44:51 +00:00
|
|
|
|
2012-04-21 11:06:26 +00:00
|
|
|
/* Record session number if required */
|
2012-07-03 05:43:39 +00:00
|
|
|
if (!call->remote.session)
|
|
|
|
call->remote.session=sender_session;
|
2012-04-21 11:06:26 +00:00
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP) {
|
|
|
|
DEBUGF("%06x:%06x matches call #%d %06x:%06x",
|
2012-04-24 05:10:30 +00:00
|
|
|
sender_session,recvr_session,i,
|
2012-07-03 05:43:39 +00:00
|
|
|
call->remote.session,
|
|
|
|
call->local.session);
|
2012-04-24 05:10:30 +00:00
|
|
|
}
|
2012-04-21 21:38:33 +00:00
|
|
|
|
2012-07-03 05:43:39 +00:00
|
|
|
return call;
|
2012-04-18 21:44:51 +00:00
|
|
|
}
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
/* Don't create a call record if either party has ended it */
|
|
|
|
if (sender_state==VOMP_STATE_CALLENDED || recvr_state==VOMP_STATE_CALLENDED)
|
2012-04-22 09:45:55 +00:00
|
|
|
return NULL;
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
/* Only create a call record if either party is in CALLPREP state */
|
|
|
|
if (sender_state==VOMP_STATE_CALLPREP || recvr_state==VOMP_STATE_CALLPREP)
|
2012-08-08 01:26:05 +00:00
|
|
|
return vomp_create_call(remote_sid, local_sid, sender_session, recvr_session);
|
2012-04-18 21:44:51 +00:00
|
|
|
|
2012-07-05 06:22:21 +00:00
|
|
|
return NULL;
|
2012-04-18 21:44:51 +00:00
|
|
|
}
|
|
|
|
|
2012-04-19 19:49:42 +00:00
|
|
|
/* send updated call status to end-point and to any interested listeners as
|
|
|
|
appropriate */
|
2012-04-21 11:51:26 +00:00
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_send_status_remote_audio(struct vomp_call_state *call, int audio_codec, const unsigned char *audio, int audio_length)
|
2012-04-18 21:44:51 +00:00
|
|
|
{
|
2012-07-05 06:22:21 +00:00
|
|
|
overlay_mdp_frame mdp;
|
|
|
|
|
|
|
|
bzero(&mdp,sizeof(mdp));
|
|
|
|
mdp.packetTypeAndFlags=MDP_TX;
|
|
|
|
bcopy(call->local.sid,mdp.out.src.sid,SID_SIZE);
|
|
|
|
mdp.out.src.port=MDP_PORT_VOMP;
|
|
|
|
bcopy(call->remote.sid,mdp.out.dst.sid,SID_SIZE);
|
|
|
|
mdp.out.dst.port=MDP_PORT_VOMP;
|
|
|
|
|
|
|
|
mdp.out.payload[0]=0x01; /* Normal VoMP frame */
|
|
|
|
mdp.out.payload[1]=(call->remote.state<<4)|call->local.state;
|
|
|
|
mdp.out.payload[2]=(call->remote.sequence>>8)&0xff;
|
|
|
|
mdp.out.payload[3]=(call->remote.sequence>>0)&0xff;
|
|
|
|
mdp.out.payload[4]=(call->local.sequence>>8)&0xff;
|
|
|
|
mdp.out.payload[5]=(call->local.sequence>>0)&0xff;
|
2012-08-09 02:44:32 +00:00
|
|
|
time_ms_t call_millis = gettime_ms() - call->create_time;
|
2012-07-05 06:22:21 +00:00
|
|
|
mdp.out.payload[6]=(call_millis>>8)&0xff;
|
|
|
|
mdp.out.payload[7]=(call_millis>>0)&0xff;
|
|
|
|
mdp.out.payload[8]=(call->remote.session>>16)&0xff;
|
|
|
|
mdp.out.payload[9]=(call->remote.session>>8)&0xff;
|
|
|
|
mdp.out.payload[10]=(call->remote.session>>0)&0xff;
|
|
|
|
mdp.out.payload[11]=(call->local.session>>16)&0xff;
|
|
|
|
mdp.out.payload[12]=(call->local.session>>8)&0xff;
|
|
|
|
mdp.out.payload[13]=(call->local.session>>0)&0xff;
|
|
|
|
|
|
|
|
mdp.out.payload_length=14;
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
if (call->local.state < VOMP_STATE_RINGINGOUT && call->remote.state < VOMP_STATE_RINGINGOUT) {
|
2012-07-05 06:22:21 +00:00
|
|
|
/* Also include list of supported codecs */
|
|
|
|
int i;
|
2012-08-02 01:59:37 +00:00
|
|
|
for (i = 0; i < 256; ++i)
|
2012-07-05 06:22:21 +00:00
|
|
|
if (vomp_local_codec_list[i]) {
|
|
|
|
mdp.out.payload[mdp.out.payload_length++]=i;
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("I support the %s codec", vomp_describe_codec(i));
|
2012-04-23 04:00:22 +00:00
|
|
|
}
|
2012-07-05 06:22:21 +00:00
|
|
|
mdp.out.payload[mdp.out.payload_length++]=0;
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("mdp frame with codec list is %d bytes", mdp.out.payload_length);
|
2012-07-05 06:22:21 +00:00
|
|
|
}
|
2012-05-06 12:53:58 +00:00
|
|
|
|
2012-07-05 06:22:21 +00:00
|
|
|
if (call->local.state==VOMP_STATE_INCALL && audio && audio_length && vomp_sample_size(audio_codec)==audio_length) {
|
|
|
|
unsigned short *len=&mdp.out.payload_length;
|
|
|
|
unsigned char *p=&mdp.out.payload[0];
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
// DEBUG("Including audio sample block");
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
/* record sample in recent list.
|
|
|
|
XXX - What timestamp to attach to the sample?
|
|
|
|
Two obvious choices:
|
|
|
|
1. The sample is for the most recent n milliseconds; or
|
|
|
|
2. The sample is for the next n milliseconds following the
|
|
|
|
last sample.
|
|
|
|
|
|
|
|
Option 1 introduces all sorts of problems with sample production
|
|
|
|
jitter, where as option 2 has no such problems, but simply requires the
|
|
|
|
producer of audio to ensure that they provide exactly the right amount
|
|
|
|
of audio, or risk the call getting out of sync. This is a fairly
|
|
|
|
reasonable expectation, or else things go to pot.
|
|
|
|
|
|
|
|
Note that in-call slew is the responsibility of the player, not the
|
|
|
|
recorder of audio. Basically if the audio queue starts to bank up,
|
|
|
|
then the player needs to drop samples.
|
|
|
|
*/
|
|
|
|
vomp_sample_block *sb=call->recent_samples;
|
|
|
|
int rotor=call->recent_sample_rotor%VOMP_MAX_RECENT_SAMPLES;
|
|
|
|
sb[rotor].codec=audio_codec;
|
|
|
|
sb[rotor].endtime=call->audio_clock+vomp_codec_timespan(sb[rotor].codec)-1;
|
|
|
|
sb[rotor].starttime=call->audio_clock;
|
|
|
|
call->audio_clock=sb[rotor].endtime+1;
|
|
|
|
bcopy(audio,&sb[rotor].bytes[0],audio_length);
|
|
|
|
|
|
|
|
/* write the sample end-time in milliseconds since call establishment */
|
|
|
|
p[(*len)++]=(call->audio_clock>>24)&0xff;
|
|
|
|
p[(*len)++]=(call->audio_clock>>16)&0xff;
|
|
|
|
p[(*len)++]=(call->audio_clock>>8)&0xff;
|
|
|
|
p[(*len)++]=(call->audio_clock>>0)&0xff;
|
|
|
|
|
|
|
|
/* stuff frame with most recent sample blocks as a form of preemptive
|
|
|
|
retransmission. But don't make the packets too large. */
|
|
|
|
while (((*len)+1+audio_length)
|
|
|
|
<VOMP_STUFF_BYTES) {
|
|
|
|
p[(*len)++]=sb[rotor].codec;
|
|
|
|
bcopy(&sb[rotor].bytes[0],&p[*len],vomp_sample_size(sb[rotor].codec));
|
|
|
|
(*len)+=vomp_sample_size(sb[rotor].codec);
|
2012-05-22 01:12:29 +00:00
|
|
|
|
2012-07-05 06:22:21 +00:00
|
|
|
rotor--; if (rotor<0) rotor+=VOMP_MAX_RECENT_SAMPLES;
|
|
|
|
rotor%=VOMP_MAX_RECENT_SAMPLES;
|
2012-04-21 11:06:26 +00:00
|
|
|
|
2012-07-05 07:06:11 +00:00
|
|
|
// stop if we've run out of samples before we ran out of bytes
|
|
|
|
if ((!sb[rotor].endtime)||(sb[rotor].endtime+1==call->audio_clock)) break;
|
2012-04-21 11:06:26 +00:00
|
|
|
}
|
2012-07-05 06:22:21 +00:00
|
|
|
call->recent_sample_rotor++;
|
|
|
|
call->recent_sample_rotor%=VOMP_MAX_RECENT_SAMPLES;
|
2012-04-19 22:32:03 +00:00
|
|
|
}
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
/* XXX Here we act as our own client. This used to be able to block.
|
|
|
|
We should really refactor overlay_mdp_poll() so that we can deliver
|
|
|
|
the frame directly.
|
|
|
|
Make sure that we don't want (just drop the message if there is
|
|
|
|
congestion) */
|
|
|
|
|
|
|
|
overlay_mdp_dispatch(&mdp,1,NULL,0);
|
|
|
|
|
|
|
|
call->local.sequence++;
|
|
|
|
return 0;
|
|
|
|
}
|
2012-04-19 22:32:03 +00:00
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_send_status_remote(struct vomp_call_state *call)
|
2012-08-02 01:59:37 +00:00
|
|
|
{
|
2012-07-05 06:22:21 +00:00
|
|
|
return vomp_send_status_remote_audio(call, 0, NULL, 0);
|
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_send_mdp_status_audio(struct vomp_call_state *call, int audio_codec, unsigned int start_time, unsigned int end_time, const unsigned char *audio, int audio_length)
|
2012-08-02 01:59:37 +00:00
|
|
|
{
|
2012-07-05 06:22:21 +00:00
|
|
|
if (audio && audio_length && vomp_sample_size(audio_codec)!=audio_length)
|
|
|
|
return WHY("Audio frame is the wrong length");
|
|
|
|
|
|
|
|
overlay_mdp_frame mdp;
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUG("Sending mdp client packet");
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
bzero(&mdp,sizeof(mdp));
|
|
|
|
mdp.packetTypeAndFlags=MDP_VOMPEVENT;
|
|
|
|
mdp.vompevent.call_session_token=call->local.session;
|
|
|
|
mdp.vompevent.last_activity=call->last_activity;
|
|
|
|
if (call->local.state==VOMP_STATE_CALLENDED)
|
|
|
|
mdp.vompevent.flags|=VOMPEVENT_CALLENDED;
|
|
|
|
if (call->remote.state==VOMPEVENT_CALLENDED)
|
|
|
|
mdp.vompevent.flags|=VOMPEVENT_CALLREJECT;
|
|
|
|
if (call->audio_started)
|
|
|
|
mdp.vompevent.flags|=VOMPEVENT_AUDIOSTREAMING;
|
|
|
|
// TODO ???
|
|
|
|
//mdp.vompevent.flags|=VOMPEVENT_CALLCREATED;
|
|
|
|
mdp.vompevent.local_state=call->local.state;
|
|
|
|
mdp.vompevent.remote_state=call->remote.state;
|
|
|
|
|
|
|
|
bcopy(&call->remote_codec_list[0],&mdp.vompevent.supported_codecs[0],256);
|
|
|
|
|
|
|
|
if (audio && audio_length) {
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("Frame contains audio (codec=%s)", vomp_describe_codec(audio_codec));
|
|
|
|
bcopy(audio, &mdp.vompevent.audio_bytes[0], audio_length);
|
2012-07-05 06:22:21 +00:00
|
|
|
mdp.vompevent.audio_sample_codec=audio_codec;
|
|
|
|
mdp.vompevent.audio_sample_bytes=audio_length;
|
|
|
|
mdp.vompevent.audio_sample_starttime=start_time;
|
|
|
|
mdp.vompevent.audio_sample_endtime=end_time;
|
2012-04-19 22:32:03 +00:00
|
|
|
}
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
int i;
|
2012-08-09 02:44:32 +00:00
|
|
|
time_ms_t now = gettime_ms();
|
2012-07-05 06:22:21 +00:00
|
|
|
for(i=0;i<vomp_interested_usock_count;i++)
|
|
|
|
if (vomp_interested_expiries[i]>=now) {
|
|
|
|
overlay_mdp_reply(mdp_named.poll.fd,
|
|
|
|
vomp_interested_usocks[i],
|
|
|
|
vomp_interested_usock_lengths[i],
|
|
|
|
&mdp);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_send_mdp_status(struct vomp_call_state *call)
|
2012-08-02 01:59:37 +00:00
|
|
|
{
|
2012-07-05 06:22:21 +00:00
|
|
|
return vomp_send_mdp_status_audio(call,0,0,0,NULL,0);
|
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int monitor_call_status(struct vomp_call_state *call)
|
|
|
|
{
|
|
|
|
char msg[1024];
|
|
|
|
int n = snprintf(msg,1024,"\nCALLSTATUS:%06x:%06x:%d:%d:%d:%s:%s:%s:%s\n",
|
|
|
|
call->local.session,call->remote.session,
|
|
|
|
call->local.state,call->remote.state,
|
|
|
|
call->fast_audio,
|
|
|
|
alloca_tohex_sid(call->local.sid),
|
|
|
|
alloca_tohex_sid(call->remote.sid),
|
|
|
|
call->local.did,call->remote.did);
|
|
|
|
|
|
|
|
monitor_tell_clients(msg, n, MONITOR_VOMP);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int monitor_send_audio(struct vomp_call_state *call, int audio_codec, unsigned int start_time, unsigned int end_time, const unsigned char *audio, int audio_length)
|
|
|
|
{
|
|
|
|
if (0) DEBUGF("Tell call monitor about audio for call %06x:%06x",
|
|
|
|
call->local.session,call->remote.session);
|
|
|
|
int sample_bytes=vomp_sample_size(audio_codec);
|
|
|
|
char msg[1024 + MAX_AUDIO_BYTES];
|
|
|
|
/* All commands followed by binary data start with *len:, so that
|
|
|
|
they can be easily parsed at the far end, even if not supported.
|
|
|
|
Put newline at start of these so that receiving data in command
|
|
|
|
mode doesn't confuse the parser. */
|
|
|
|
int msglen = snprintf(msg, 1024,
|
2012-08-08 01:26:05 +00:00
|
|
|
"\n*%d:AUDIOPACKET:%x:%d:%d:%d\n",
|
2012-08-06 05:16:46 +00:00
|
|
|
sample_bytes,
|
2012-08-08 01:26:05 +00:00
|
|
|
call->local.session,
|
2012-08-06 05:16:46 +00:00
|
|
|
audio_codec, start_time, end_time);
|
|
|
|
|
|
|
|
bcopy(audio, &msg[msglen], sample_bytes);
|
|
|
|
msglen+=sample_bytes;
|
|
|
|
msg[msglen++]='\n';
|
|
|
|
monitor_tell_clients(msg, msglen, MONITOR_VOMP);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-08 01:26:05 +00:00
|
|
|
// update local state and notify interested clients with the correct message
|
|
|
|
int vomp_update_local_state(struct vomp_call_state *call, int new_state){
|
|
|
|
if (call->local.state>=new_state)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch(new_state){
|
|
|
|
case VOMP_STATE_CALLPREP:
|
|
|
|
// tell client our session id.
|
2012-08-09 02:23:45 +00:00
|
|
|
monitor_tell_formatted(MONITOR_VOMP, "\nCALLTO:%06x:%s:%s:%s:%s\n",
|
2012-08-08 01:26:05 +00:00
|
|
|
call->local.session,
|
|
|
|
alloca_tohex_sid(call->local.sid), call->local.did,
|
|
|
|
alloca_tohex_sid(call->remote.sid), call->remote.did);
|
|
|
|
break;
|
|
|
|
case VOMP_STATE_CALLENDED:
|
2012-08-09 02:23:45 +00:00
|
|
|
monitor_tell_formatted(MONITOR_VOMP, "\nHANGUP:%06x\n", call->local.session);
|
2012-08-08 01:26:05 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
call->local.state=new_state;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// update remote state and notify interested clients with the correct message
|
|
|
|
int vomp_update_remote_state(struct vomp_call_state *call, int new_state){
|
|
|
|
if (call->remote.state>=new_state)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch(new_state){
|
|
|
|
case VOMP_STATE_RINGINGOUT:
|
2012-08-09 02:23:45 +00:00
|
|
|
monitor_tell_formatted(MONITOR_VOMP, "\nCALLFROM:%06x:%s:%s:%s:%s\n",
|
2012-08-08 01:26:05 +00:00
|
|
|
call->local.session,
|
|
|
|
alloca_tohex_sid(call->local.sid), call->local.did,
|
|
|
|
alloca_tohex_sid(call->remote.sid), call->remote.did);
|
|
|
|
break;
|
|
|
|
case VOMP_STATE_RINGINGIN:
|
2012-08-09 02:23:45 +00:00
|
|
|
monitor_tell_formatted(MONITOR_VOMP, "\nRINGING:%06x\n", call->local.session);
|
2012-08-08 01:26:05 +00:00
|
|
|
break;
|
|
|
|
case VOMP_STATE_INCALL:
|
|
|
|
if (call->remote.state==VOMP_STATE_RINGINGIN){
|
2012-08-09 02:23:45 +00:00
|
|
|
monitor_tell_formatted(MONITOR_VOMP, "\nANSWERED:%06x\n", call->local.session);
|
2012-08-08 01:26:05 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
call->remote.state=new_state;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-05 06:22:21 +00:00
|
|
|
// send call state updates if required.
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_update(struct vomp_call_state *call)
|
2012-08-02 01:59:37 +00:00
|
|
|
{
|
2012-07-05 06:22:21 +00:00
|
|
|
int combined_status=(call->remote.state<<4)|call->local.state;
|
|
|
|
|
|
|
|
if (call->last_sent_status==combined_status)
|
|
|
|
return 0;
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("Call state changed to %d %d, sending updates",call->local.state, call->remote.state);
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
call->last_sent_status=combined_status;
|
|
|
|
|
|
|
|
// tell the remote party
|
|
|
|
vomp_send_status_remote(call);
|
|
|
|
|
|
|
|
// tell monitor clients
|
2012-08-06 05:16:46 +00:00
|
|
|
if (monitor_socket_count && monitor_client_interested(MONITOR_VOMP))
|
2012-07-05 06:22:21 +00:00
|
|
|
monitor_call_status(call);
|
|
|
|
|
|
|
|
// tell mdp clients
|
|
|
|
if (vomp_interested_usock_count)
|
|
|
|
vomp_send_mdp_status(call);
|
|
|
|
|
2012-04-19 22:32:03 +00:00
|
|
|
return 0;
|
2012-04-18 21:44:51 +00:00
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_call_start_audio(struct vomp_call_state *call)
|
2012-04-18 21:44:51 +00:00
|
|
|
{
|
2012-04-22 09:45:55 +00:00
|
|
|
call->audio_started=1;
|
2012-04-18 21:44:51 +00:00
|
|
|
return WHY("Not implemented");
|
|
|
|
}
|
|
|
|
|
2012-07-05 06:22:21 +00:00
|
|
|
// check a small circular buffer of recently seen audio
|
|
|
|
// we're not trying to be perfect here, we still expect all clients to reorder and filter duplicates
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_audio_already_seen(struct vomp_call_state *call, unsigned int end_time)
|
2012-08-02 01:59:37 +00:00
|
|
|
{
|
2012-07-05 06:22:21 +00:00
|
|
|
int i;
|
|
|
|
for(i=0;i<VOMP_MAX_RECENT_SAMPLES *4;i++)
|
|
|
|
if (call->seen_samples[i]==end_time)
|
|
|
|
return 1;
|
|
|
|
call->seen_samples[call->sample_pos]=end_time;
|
|
|
|
call->sample_pos++;
|
|
|
|
if (call->sample_pos>=VOMP_MAX_RECENT_SAMPLES *4)
|
|
|
|
call->sample_pos=0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_process_audio(struct vomp_call_state *call,unsigned int sender_duration,overlay_mdp_frame *mdp)
|
2012-04-18 21:44:51 +00:00
|
|
|
{
|
2012-04-24 09:03:40 +00:00
|
|
|
int ofs=14;
|
2012-05-08 22:05:05 +00:00
|
|
|
// if (mdp->in.payload_length>14)
|
2012-08-02 01:59:37 +00:00
|
|
|
// DEBUGF("got here (payload has %d bytes)",mdp->in.payload_length);
|
2012-04-24 10:56:16 +00:00
|
|
|
|
|
|
|
/* Get end time marker for sample block collection */
|
2012-07-06 00:44:39 +00:00
|
|
|
unsigned int e=0, s=0;
|
2012-04-24 10:56:16 +00:00
|
|
|
e=mdp->in.payload[ofs++]<<24;
|
|
|
|
e|=mdp->in.payload[ofs++]<<16;
|
|
|
|
e|=mdp->in.payload[ofs++]<<8;
|
|
|
|
e|=mdp->in.payload[ofs++]<<0;
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
sender_duration = (e&0xFFFF0000)|sender_duration;
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
2012-08-09 02:44:32 +00:00
|
|
|
DEBUGF("Jitter %d, %lld", sender_duration - e, (long long)((gettime_ms() - call->create_time) - e));
|
2012-07-05 06:22:21 +00:00
|
|
|
|
2012-04-24 09:03:40 +00:00
|
|
|
while(ofs<mdp->in.payload_length)
|
|
|
|
{
|
|
|
|
int codec=mdp->in.payload[ofs];
|
2012-08-02 01:59:37 +00:00
|
|
|
// DEBUGF("Spotted a %s sample block",vomp_describe_codec(codec));
|
2012-04-24 09:03:40 +00:00
|
|
|
if (!codec||vomp_sample_size(codec)<0) break;
|
2012-04-24 09:25:57 +00:00
|
|
|
if ((ofs+1+vomp_sample_size(codec))>mdp->in.payload_length) break;
|
|
|
|
|
2012-04-24 10:56:16 +00:00
|
|
|
/* work out start-time from end-time less duration of included sample(s).
|
|
|
|
XXX - Assumes only non-adaptive codecs. */
|
2012-07-05 06:22:21 +00:00
|
|
|
s = e-vomp_codec_timespan(codec)+1;
|
2012-04-18 21:44:51 +00:00
|
|
|
|
2012-04-24 09:03:40 +00:00
|
|
|
/* Pass audio frame to all registered listeners */
|
2012-07-05 06:22:21 +00:00
|
|
|
if (!vomp_audio_already_seen(call, e)){
|
|
|
|
if (vomp_interested_usock_count)
|
|
|
|
vomp_send_mdp_status_audio(call, codec, s, e,
|
|
|
|
&mdp->in.payload[ofs+1],
|
|
|
|
vomp_sample_size(codec)
|
|
|
|
);
|
2012-04-24 09:03:40 +00:00
|
|
|
|
2012-07-05 06:22:21 +00:00
|
|
|
if (monitor_socket_count)
|
|
|
|
monitor_send_audio(call, codec, s, e,
|
|
|
|
&mdp->in.payload[ofs+1],
|
|
|
|
vomp_sample_size(codec)
|
|
|
|
);
|
|
|
|
}
|
2012-04-24 09:03:40 +00:00
|
|
|
ofs+=1+vomp_sample_size(codec);
|
2012-07-05 06:22:21 +00:00
|
|
|
e=s-1;
|
2012-04-24 09:03:40 +00:00
|
|
|
}
|
2012-04-24 05:10:30 +00:00
|
|
|
return 0;
|
2012-04-18 21:44:51 +00:00
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_call_stop_audio(struct vomp_call_state *call)
|
2012-04-18 21:44:51 +00:00
|
|
|
{
|
2012-04-22 09:45:55 +00:00
|
|
|
call->audio_started=0;
|
2012-04-18 21:44:51 +00:00
|
|
|
return WHY("Not implemented");
|
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_ringing(struct vomp_call_state *call){
|
2012-07-31 06:15:24 +00:00
|
|
|
if (call){
|
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("RING RING!");
|
|
|
|
if (call->local.state<VOMP_STATE_RINGINGIN && call->remote.state==VOMP_STATE_RINGINGOUT){
|
2012-08-08 01:26:05 +00:00
|
|
|
vomp_update_local_state(call, VOMP_STATE_RINGINGIN);
|
2012-07-31 06:15:24 +00:00
|
|
|
vomp_update(call);
|
|
|
|
}
|
|
|
|
}
|
2012-04-19 20:25:58 +00:00
|
|
|
return 0;
|
2012-04-18 21:44:51 +00:00
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_call_destroy(struct vomp_call_state *call)
|
2012-04-18 21:44:51 +00:00
|
|
|
{
|
2012-04-19 20:25:58 +00:00
|
|
|
/* do some general clean ups */
|
2012-04-18 21:44:51 +00:00
|
|
|
if (call->audio_started) vomp_call_stop_audio(call);
|
2012-04-19 20:25:58 +00:00
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("Destroying call %s <--> %s", call->local.did,call->remote.did);
|
2012-04-19 20:25:58 +00:00
|
|
|
|
2012-04-22 09:45:55 +00:00
|
|
|
/* tell everyone the call has died */
|
2012-08-08 01:26:05 +00:00
|
|
|
vomp_update_local_state(call, VOMP_STATE_CALLENDED);
|
2012-07-05 06:22:21 +00:00
|
|
|
vomp_update(call);
|
2012-04-22 09:45:55 +00:00
|
|
|
|
2012-04-19 20:25:58 +00:00
|
|
|
/* now release the call structure */
|
2012-07-03 05:43:39 +00:00
|
|
|
int i = (call - vomp_call_states);
|
|
|
|
unschedule(&call->alarm);
|
|
|
|
|
|
|
|
vomp_call_count--;
|
|
|
|
if (i!=vomp_call_count){
|
|
|
|
unschedule(&vomp_call_states[vomp_call_count].alarm);
|
|
|
|
bcopy(&vomp_call_states[vomp_call_count],
|
|
|
|
call,
|
2012-08-06 05:16:46 +00:00
|
|
|
sizeof(struct vomp_call_state));
|
2012-07-03 05:43:39 +00:00
|
|
|
schedule(&call->alarm);
|
|
|
|
}
|
2012-04-19 20:25:58 +00:00
|
|
|
return 0;
|
2012-04-18 21:44:51 +00:00
|
|
|
}
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
int vomp_dial(unsigned char *local_sid, unsigned char *remote_sid, char *local_did, char *remote_did)
|
|
|
|
{
|
2012-07-05 06:22:21 +00:00
|
|
|
/* TODO use local_did and remote_did start putting the call together.
|
|
|
|
These need to be passed to the node being called to provide caller id,
|
|
|
|
and potentially handle call-routing, e.g., if it is a gateway.
|
|
|
|
*/
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUG("Dialing");
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
if (vomp_call_count>=VOMP_MAX_CALLS)
|
|
|
|
return WHY("All call slots in use");
|
|
|
|
|
|
|
|
/* allocate unique call session token, which is how the client will
|
|
|
|
refer to this call during its life */
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *call=vomp_create_call(
|
2012-07-05 06:22:21 +00:00
|
|
|
remote_sid,
|
|
|
|
local_sid,
|
|
|
|
0,
|
2012-08-08 01:26:05 +00:00
|
|
|
0);
|
|
|
|
vomp_update_local_state(call, VOMP_STATE_CALLPREP);
|
2012-07-05 06:22:21 +00:00
|
|
|
// remember that we initiated this call, not the other party
|
|
|
|
call->initiated_call = 1;
|
|
|
|
|
|
|
|
/* send status update to remote, thus causing call to be created
|
|
|
|
(hopefully) at far end. */
|
|
|
|
vomp_update(call);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_pickup(struct vomp_call_state *call)
|
2012-08-02 01:59:37 +00:00
|
|
|
{
|
2012-07-05 06:22:21 +00:00
|
|
|
if (call){
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug && DEBUG_VOMP)
|
|
|
|
DEBUG("Picking up");
|
2012-07-05 06:22:21 +00:00
|
|
|
if (call->local.state!=VOMP_STATE_RINGINGIN)
|
|
|
|
return WHY("Call is not ringing");
|
2012-08-08 01:26:05 +00:00
|
|
|
vomp_update_local_state(call, VOMP_STATE_INCALL);
|
2012-07-30 07:52:38 +00:00
|
|
|
call->create_time=gettime_ms();
|
2012-07-05 06:22:21 +00:00
|
|
|
/* state machine does job of starting audio stream, just tell everyone about
|
|
|
|
the changed state. */
|
|
|
|
vomp_update(call);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_hangup(struct vomp_call_state *call)
|
2012-08-02 01:59:37 +00:00
|
|
|
{
|
2012-07-05 06:22:21 +00:00
|
|
|
if (call){
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUG("Hanging up");
|
2012-07-05 06:22:21 +00:00
|
|
|
if (call->local.state==VOMP_STATE_INCALL) vomp_call_stop_audio(call);
|
2012-08-08 01:26:05 +00:00
|
|
|
vomp_update_local_state(call, VOMP_STATE_CALLENDED);
|
2012-07-05 06:22:21 +00:00
|
|
|
vomp_update(call);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-18 23:17:59 +00:00
|
|
|
/* An MDP message of type MDP_VOMPEVENT received from the unix domain socket.
|
|
|
|
This is how user tasks request telephone calls and receive updated status
|
|
|
|
and audio from the call. We need the receiver socket so that we can
|
|
|
|
route the events back to wherever they should be going.
|
|
|
|
XXX - We should have some means of authenticating/protecting this interface
|
|
|
|
so that any old process cannot request a mesh call. Although, in fairness,
|
|
|
|
the user will know about the call because the call display will come up.
|
|
|
|
*/
|
2012-04-23 04:00:22 +00:00
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
int vomp_mdp_event(overlay_mdp_frame *mdp, struct sockaddr_un *recvaddr,int recvaddrlen)
|
2012-04-18 23:17:59 +00:00
|
|
|
{
|
2012-04-19 12:23:40 +00:00
|
|
|
/* Frames from the user can take only a few forms:
|
|
|
|
- announce interest in call state.
|
|
|
|
- withdraw interest in call state.
|
|
|
|
- place a call (SID+DID combination)
|
|
|
|
- deliver audio for sending
|
|
|
|
- indicate pickup, hangup or call reject
|
|
|
|
|
|
|
|
We then send back all sorts of relevant call state information as well as
|
|
|
|
transported audio. In particular we inform when the call state changes,
|
|
|
|
including if any error has occurred.
|
|
|
|
*/
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("Flags=0x%x",mdp->vompevent.flags);
|
|
|
|
|
2012-04-19 12:23:40 +00:00
|
|
|
switch(mdp->vompevent.flags)
|
|
|
|
{
|
|
|
|
case VOMPEVENT_REGISTERINTEREST:
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUG("Request to register interest");
|
2012-04-19 12:23:40 +00:00
|
|
|
/* put unix domain socket on record to send call state event and audio to. */
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int candidate=-1;
|
2012-08-09 02:44:32 +00:00
|
|
|
time_ms_t now = gettime_ms();
|
2012-04-19 12:23:40 +00:00
|
|
|
for(i=0;i<vomp_interested_usock_count;i++)
|
|
|
|
{
|
|
|
|
if (vomp_interested_usock_lengths[i]==recvaddrlen)
|
|
|
|
if (!memcmp(recvaddr->sun_path,
|
|
|
|
vomp_interested_usocks[i],recvaddrlen))
|
|
|
|
/* found it -- so we are already monitoring this one */
|
2012-07-02 03:49:54 +00:00
|
|
|
return overlay_mdp_reply_error(mdp_named.poll.fd,recvaddr,recvaddrlen,
|
2012-04-19 12:23:40 +00:00
|
|
|
0,"Success");
|
|
|
|
if (vomp_interested_expiries[i]<now) candidate=i;
|
|
|
|
}
|
|
|
|
if (i>=vomp_interested_usock_count&&(candidate>-1)) i=candidate;
|
|
|
|
/* not currently on the list, so add */
|
|
|
|
if (i<VOMP_MAX_INTERESTED) {
|
|
|
|
if (vomp_interested_usocks[i]) {
|
|
|
|
free(vomp_interested_usocks[i]);
|
|
|
|
vomp_interested_usocks[i]=NULL;
|
|
|
|
}
|
|
|
|
vomp_interested_usocks[i]=malloc(recvaddrlen);
|
|
|
|
if (!vomp_interested_usocks[i])
|
2012-07-02 03:49:54 +00:00
|
|
|
return overlay_mdp_reply_error(mdp_named.poll.fd, recvaddr,recvaddrlen,
|
2012-04-19 12:23:40 +00:00
|
|
|
4002,"Out of memory");
|
|
|
|
bcopy(recvaddr,vomp_interested_usocks[i],
|
|
|
|
recvaddrlen);
|
|
|
|
vomp_interested_usock_lengths[i]=recvaddrlen;
|
2012-08-09 02:44:32 +00:00
|
|
|
vomp_interested_expiries[i] = gettime_ms() + 60000;
|
2012-04-19 12:23:40 +00:00
|
|
|
if (i==vomp_interested_usock_count) vomp_interested_usock_count++;
|
2012-04-23 04:00:22 +00:00
|
|
|
|
|
|
|
if (mdp->vompevent.supported_codecs[0]) {
|
|
|
|
/* Replace set of locally supported codecs */
|
|
|
|
for(i=0;i<256;i++) vomp_local_codec_list[i]=0;
|
|
|
|
for(i=0;(i<256)&&mdp->vompevent.supported_codecs[i];i++)
|
2012-04-23 07:22:10 +00:00
|
|
|
{
|
|
|
|
vomp_local_codec_list[mdp->vompevent.supported_codecs[i]]=1;
|
|
|
|
}
|
2012-04-23 04:00:22 +00:00
|
|
|
}
|
|
|
|
|
2012-04-22 09:17:24 +00:00
|
|
|
return overlay_mdp_reply_error
|
2012-07-02 03:49:54 +00:00
|
|
|
(mdp_named.poll.fd,recvaddr,recvaddrlen,0,"Success");
|
2012-04-19 12:23:40 +00:00
|
|
|
} else {
|
|
|
|
return overlay_mdp_reply_error
|
2012-07-02 03:49:54 +00:00
|
|
|
(mdp_named.poll.fd,recvaddr,recvaddrlen,
|
2012-04-19 12:23:40 +00:00
|
|
|
4003,"Too many listeners (try again in a minute?)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case VOMPEVENT_WITHDRAWINTEREST:
|
|
|
|
/* opposite of above */
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUG("Request to withdraw interest");
|
2012-04-19 12:23:40 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i=0;i<vomp_interested_usock_count;i++)
|
|
|
|
{
|
|
|
|
if (vomp_interested_usock_lengths[i]==recvaddrlen)
|
|
|
|
if (!memcmp(recvaddr->sun_path,
|
|
|
|
vomp_interested_usocks[i],recvaddrlen))
|
|
|
|
{
|
|
|
|
/* found it -- so we are already monitoring this one */
|
|
|
|
free(vomp_interested_usocks[i]);
|
|
|
|
if (i<vomp_interested_usock_count-1)
|
|
|
|
{
|
|
|
|
int swap=vomp_interested_usock_count-1;
|
|
|
|
vomp_interested_usock_lengths[i]
|
|
|
|
=vomp_interested_usock_lengths[swap];
|
|
|
|
vomp_interested_usocks[i]=vomp_interested_usocks[swap];
|
2012-08-09 02:44:32 +00:00
|
|
|
vomp_interested_expiries[i] = vomp_interested_expiries[swap];
|
2012-04-19 12:23:40 +00:00
|
|
|
}
|
|
|
|
vomp_interested_usock_count--;
|
|
|
|
return overlay_mdp_reply_error
|
2012-07-02 03:49:54 +00:00
|
|
|
(mdp_named.poll.fd,recvaddr,recvaddrlen,
|
2012-04-19 12:23:40 +00:00
|
|
|
0,"Success. You have been removed.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return overlay_mdp_reply_error
|
2012-07-02 03:49:54 +00:00
|
|
|
(mdp_named.poll.fd,recvaddr,recvaddrlen,
|
2012-04-19 12:23:40 +00:00
|
|
|
0,"Success. You were never listening.");
|
|
|
|
}
|
|
|
|
break;
|
2012-04-19 20:08:45 +00:00
|
|
|
case VOMPEVENT_CALLINFO:
|
|
|
|
{
|
|
|
|
/* provide call endpoint info to user */
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *call
|
2012-04-19 20:08:45 +00:00
|
|
|
=vomp_find_call_by_session(mdp->vompevent.call_session_token);
|
|
|
|
|
|
|
|
/* collect call info and send to requestor */
|
|
|
|
overlay_mdp_frame mdpreply;
|
|
|
|
bzero(&mdpreply,sizeof(mdpreply));
|
|
|
|
mdpreply.packetTypeAndFlags=MDP_VOMPEVENT;
|
|
|
|
mdpreply.vompevent.flags=VOMPEVENT_CALLINFO;
|
2012-04-21 23:18:23 +00:00
|
|
|
mdpreply.vompevent.call_session_token=mdp->vompevent.call_session_token;
|
2012-04-19 20:17:31 +00:00
|
|
|
if (call) {
|
|
|
|
if (call->audio_started)
|
|
|
|
mdpreply.vompevent.flags|=VOMPEVENT_AUDIOSTREAMING;
|
|
|
|
if (call->remote.state==VOMP_STATE_CALLENDED)
|
|
|
|
mdpreply.vompevent.flags|=VOMPEVENT_CALLENDED;
|
|
|
|
bcopy(call->local.sid,mdpreply.vompevent.local_sid,SID_SIZE);
|
|
|
|
bcopy(call->remote.sid,mdpreply.vompevent.remote_sid,SID_SIZE);
|
|
|
|
bcopy(call->local.did,mdpreply.vompevent.local_did,64);
|
|
|
|
bcopy(call->remote.did,mdpreply.vompevent.remote_did,64);
|
2012-05-08 22:05:05 +00:00
|
|
|
dump_vomp_status();
|
2012-04-19 20:17:31 +00:00
|
|
|
} else
|
|
|
|
if (mdp->vompevent.call_session_token)
|
|
|
|
/* let the requestor know that the requested call doesn't exist */
|
|
|
|
mdpreply.vompevent.flags|=VOMPEVENT_ERROR;
|
2012-04-19 20:08:45 +00:00
|
|
|
|
2012-04-19 20:14:51 +00:00
|
|
|
/* and provide a quick summary of all calls in progress */
|
|
|
|
int i;
|
2012-07-03 05:43:39 +00:00
|
|
|
for(i=0;i<vomp_call_count;i++)
|
2012-04-19 20:14:51 +00:00
|
|
|
{
|
2012-07-03 05:43:39 +00:00
|
|
|
mdpreply.vompevent.other_calls_sessions[i]
|
|
|
|
=vomp_call_states[i].local.session;
|
|
|
|
mdpreply.vompevent.other_calls_states[i]
|
|
|
|
=vomp_call_states[i].local.state;
|
2012-04-19 20:14:51 +00:00
|
|
|
}
|
2012-05-02 17:30:34 +00:00
|
|
|
|
2012-07-02 03:49:54 +00:00
|
|
|
return overlay_mdp_reply(mdp_named.poll.fd,recvaddr,recvaddrlen,&mdpreply);
|
2012-04-19 20:08:45 +00:00
|
|
|
}
|
|
|
|
break;
|
2012-07-05 06:22:21 +00:00
|
|
|
case VOMPEVENT_DIAL:
|
|
|
|
if (vomp_dial(
|
|
|
|
mdp->vompevent.local_sid,
|
|
|
|
mdp->vompevent.remote_sid,
|
|
|
|
NULL,
|
|
|
|
NULL))
|
|
|
|
return overlay_mdp_reply_error
|
|
|
|
(mdp_named.poll.fd,recvaddr,recvaddrlen,4004,
|
|
|
|
"Unable to place call");
|
|
|
|
else{
|
2012-04-21 23:01:19 +00:00
|
|
|
int result= overlay_mdp_reply_error
|
2012-07-02 03:49:54 +00:00
|
|
|
(mdp_named.poll.fd,recvaddr,recvaddrlen,0, "Success");
|
2012-08-02 01:59:37 +00:00
|
|
|
if (result)
|
|
|
|
WHY("Failed to send MDP reply");
|
2012-04-21 23:01:19 +00:00
|
|
|
return result;
|
2012-04-19 19:49:42 +00:00
|
|
|
}
|
|
|
|
break;
|
2012-04-19 12:23:40 +00:00
|
|
|
case VOMPEVENT_CALLREJECT: /* hangup is the same */
|
2012-04-19 19:49:42 +00:00
|
|
|
{
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *call
|
2012-04-19 19:49:42 +00:00
|
|
|
=vomp_find_call_by_session(mdp->vompevent.call_session_token);
|
2012-08-02 01:59:37 +00:00
|
|
|
if (!call)
|
|
|
|
return overlay_mdp_reply_error(mdp_named.poll.fd,recvaddr, recvaddrlen, 4006, "No such call");
|
2012-07-05 06:22:21 +00:00
|
|
|
vomp_hangup(call);
|
2012-08-02 01:59:37 +00:00
|
|
|
return overlay_mdp_reply_error(mdp_named.poll.fd, recvaddr,recvaddrlen,0,"Success");
|
2012-04-19 19:49:42 +00:00
|
|
|
}
|
2012-04-22 03:21:58 +00:00
|
|
|
break;
|
2012-08-02 01:59:37 +00:00
|
|
|
case VOMPEVENT_PICKUP:
|
2012-04-19 19:55:48 +00:00
|
|
|
{
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *call = vomp_find_call_by_session(mdp->vompevent.call_session_token);
|
2012-08-02 01:59:37 +00:00
|
|
|
if (!call)
|
2012-04-19 19:55:48 +00:00
|
|
|
return overlay_mdp_reply_error
|
2012-07-02 03:49:54 +00:00
|
|
|
(mdp_named.poll.fd,recvaddr,recvaddrlen,4006,
|
2012-04-19 19:55:48 +00:00
|
|
|
"No such call");
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
if (vomp_pickup(call))
|
|
|
|
return overlay_mdp_reply_error(mdp_named.poll.fd,
|
|
|
|
recvaddr,recvaddrlen,4009,
|
|
|
|
"Call is not RINGINGIN, so cannot be picked up");
|
|
|
|
else
|
|
|
|
return overlay_mdp_reply_error(mdp_named.poll.fd,
|
2012-04-23 04:00:22 +00:00
|
|
|
recvaddr,recvaddrlen,0,"Success");
|
2012-04-19 19:55:48 +00:00
|
|
|
}
|
|
|
|
break;
|
2012-04-24 05:10:30 +00:00
|
|
|
case VOMPEVENT_AUDIOPACKET: /* user supplying audio */
|
2012-04-24 05:57:20 +00:00
|
|
|
{
|
2012-08-02 01:59:37 +00:00
|
|
|
// DEBUG("Audio packet arrived");
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *call = vomp_find_call_by_session(mdp->vompevent.call_session_token);
|
2012-05-06 12:53:58 +00:00
|
|
|
if (call) {
|
2012-07-05 06:22:21 +00:00
|
|
|
return vomp_send_status_remote_audio(call,
|
|
|
|
mdp->vompevent.audio_sample_codec,
|
|
|
|
&mdp->vompevent.audio_bytes[0],
|
|
|
|
vomp_sample_size(mdp->vompevent.audio_sample_codec));
|
2012-05-06 12:53:58 +00:00
|
|
|
}
|
2012-08-02 01:59:37 +00:00
|
|
|
else
|
|
|
|
return WHY("audio packet had invalid call session token");
|
2012-04-24 05:57:20 +00:00
|
|
|
}
|
2012-04-19 19:49:42 +00:00
|
|
|
break;
|
2012-04-19 12:23:40 +00:00
|
|
|
default:
|
|
|
|
/* didn't understand it, so respond with an error */
|
2012-07-02 03:49:54 +00:00
|
|
|
return overlay_mdp_reply_error(mdp_named.poll.fd,
|
2012-04-19 12:23:40 +00:00
|
|
|
recvaddr,recvaddrlen,4001,
|
|
|
|
"Invalid VOMPEVENT request (use DIAL,HANGUP,CALLREJECT,AUDIOSTREAMING,REGISTERINTERST,WITHDRAWINTERST only)");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-04-18 23:17:59 +00:00
|
|
|
return WHY("Not implemented");
|
|
|
|
}
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
int vomp_extract_remote_codec_list(struct vomp_call_state *call,overlay_mdp_frame *mdp)
|
2012-04-23 07:22:10 +00:00
|
|
|
{
|
|
|
|
int i;
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
dump("codec list mdp frame", (unsigned char *)&mdp->in.payload[0],mdp->in.payload_length);
|
|
|
|
for(i=0;mdp->in.payload[14+i]&&(i<256) &&((14+i)<mdp->in.payload_length);i++) {
|
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("populating remote codec list with %s", vomp_describe_codec(mdp->in.payload[14+i]));
|
|
|
|
call->remote_codec_list[mdp->in.payload[14+i]]=1;
|
2012-04-24 05:10:30 +00:00
|
|
|
}
|
2012-07-05 06:22:21 +00:00
|
|
|
// TODO send codec list to monitor clients
|
2012-04-23 07:22:10 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-18 21:44:51 +00:00
|
|
|
/* At this point we know the MDP frame is addressed to the VoMP port, but
|
|
|
|
we have not inspected the contents. As these frames are wire-format, we
|
|
|
|
must pay attention to endianness. */
|
2012-04-18 23:17:59 +00:00
|
|
|
int vomp_mdp_received(overlay_mdp_frame *mdp)
|
2012-04-18 21:44:51 +00:00
|
|
|
{
|
|
|
|
if (mdp->packetTypeAndFlags&(MDP_NOCRYPT|MDP_NOSIGN))
|
|
|
|
{
|
|
|
|
/* stream-crypted audio frame */
|
2012-04-18 23:17:59 +00:00
|
|
|
return WHY("not implemented");
|
2012-04-18 21:44:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* only auth-crypted frames make it this far */
|
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *call=NULL;
|
2012-04-18 21:44:51 +00:00
|
|
|
|
|
|
|
switch(mdp->in.payload[0]) {
|
|
|
|
case 0x01: /* Ordinary VoMP state+optional audio frame */
|
|
|
|
{
|
2012-04-22 09:45:55 +00:00
|
|
|
int recvr_state=mdp->in.payload[1]>>4;
|
2012-04-21 11:06:26 +00:00
|
|
|
int sender_state=mdp->in.payload[1]&0xf;
|
2012-04-18 21:44:51 +00:00
|
|
|
unsigned int recvr_session=
|
2012-07-05 06:22:21 +00:00
|
|
|
(mdp->in.payload[8]<<16)|(mdp->in.payload[9]<<8)|mdp->in.payload[10];
|
2012-04-18 21:44:51 +00:00
|
|
|
unsigned int sender_session=
|
2012-07-05 06:22:21 +00:00
|
|
|
(mdp->in.payload[11]<<16)|(mdp->in.payload[12]<<8)|mdp->in.payload[13];
|
2012-04-19 22:32:03 +00:00
|
|
|
int sender_seq=(mdp->in.payload[4]<<8)+mdp->in.payload[5];
|
2012-04-18 21:44:51 +00:00
|
|
|
|
2012-07-05 06:22:21 +00:00
|
|
|
// cyclic ~1 minute timer...
|
|
|
|
unsigned int sender_duration = (mdp->in.payload[6]<<8) | mdp->in.payload[7];
|
|
|
|
|
|
|
|
/* wants to create a call session.
|
|
|
|
Main aim here: replay protection. An adversary should not be able to
|
|
|
|
replay previous VoMP packets to cause any action. We do this by
|
|
|
|
allocating a new session number for each call. As an adversary may be
|
|
|
|
trying to use such replays to cause a denial of service attack we need
|
|
|
|
to be able to track multiple potential session numbers even from the
|
|
|
|
same SID. */
|
|
|
|
|
|
|
|
call=vomp_find_or_create_call(mdp->in.src.sid,mdp->in.dst.sid,
|
|
|
|
sender_session,recvr_session,
|
|
|
|
sender_state,recvr_state);
|
|
|
|
|
|
|
|
if (!call)
|
|
|
|
return WHY("Unable to find or create call");
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
if (!recvr_session && (debug & DEBUG_VOMP))
|
|
|
|
DEBUG("recvr_session==0, created call");
|
2012-07-05 06:22:21 +00:00
|
|
|
|
2012-08-08 01:26:05 +00:00
|
|
|
recvr_state = call->local.state;
|
2012-08-06 05:16:46 +00:00
|
|
|
|
2012-07-05 06:22:21 +00:00
|
|
|
// TODO ignore state changes if sequence is stale?
|
|
|
|
// TODO ignore state changes that seem to go backwards?
|
|
|
|
|
|
|
|
if ((!vomp_interested_usock_count)
|
2012-08-06 05:16:46 +00:00
|
|
|
&&(!monitor_socket_count)
|
|
|
|
&&(!monitor_client_interested(MONITOR_VOMP)))
|
2012-08-08 01:26:05 +00:00
|
|
|
{
|
2012-07-05 06:22:21 +00:00
|
|
|
/* No registered listener, so we cannot answer the call, so just reject
|
|
|
|
it. */
|
2012-08-08 01:26:05 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("Rejecting call due to lack of a listener: states=%d,%d", recvr_state, sender_state);
|
2012-04-23 04:00:22 +00:00
|
|
|
|
2012-08-08 01:26:05 +00:00
|
|
|
recvr_state=VOMP_STATE_CALLENDED;
|
2012-07-05 06:22:21 +00:00
|
|
|
/* now let the state machine progress to destroy the call */
|
|
|
|
}
|
2012-05-06 23:01:53 +00:00
|
|
|
|
2012-08-08 01:26:05 +00:00
|
|
|
if (recvr_state < VOMP_STATE_RINGINGOUT && sender_state < VOMP_STATE_RINGINGOUT){
|
2012-07-05 06:22:21 +00:00
|
|
|
// the other party should have given us their list of supported codecs
|
|
|
|
vomp_extract_remote_codec_list(call,mdp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sender_state==VOMP_STATE_CALLENDED){
|
|
|
|
/* For whatever reason, the far end has given up on the call,
|
|
|
|
so we must also move to CALLENDED no matter what state we were in */
|
|
|
|
|
|
|
|
if (call->audio_started) vomp_call_stop_audio(call);
|
2012-08-08 01:26:05 +00:00
|
|
|
recvr_state=VOMP_STATE_CALLENDED;
|
2012-07-05 06:22:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Consider states: our actual state, sender state, what the sender thinks
|
|
|
|
our state is, and what we think the sender's state is. But largely it
|
|
|
|
breaks down to what we think our state is, and what they think their
|
|
|
|
state is. That leaves us with just 6X6=36 cases.
|
|
|
|
*/
|
2012-08-08 01:26:05 +00:00
|
|
|
int combined_state=recvr_state<<3 | sender_state;
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
switch(combined_state) {
|
|
|
|
case (VOMP_STATE_NOCALL<<3)|VOMP_STATE_CALLPREP:
|
|
|
|
/* The remote party is in the call-prep state tryng to dial us.
|
|
|
|
We'll send them our codec list, then they can tell us to ring.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (VOMP_STATE_RINGINGIN<<3)|VOMP_STATE_RINGINGOUT:
|
|
|
|
/* they are ringing us and we are ringing. Lets keep doing that. */
|
|
|
|
case (VOMP_STATE_NOCALL<<3)|VOMP_STATE_RINGINGOUT:
|
|
|
|
/* We have have issued a session, the remote party is now indicating
|
|
|
|
that they would like us to start ringing.
|
|
|
|
So change our state to RINGINGIN. */
|
|
|
|
|
|
|
|
if (call->initiated_call)
|
|
|
|
// hey, quit it, we were trying to call you.
|
2012-08-08 01:26:05 +00:00
|
|
|
recvr_state=VOMP_STATE_CALLENDED;
|
2012-07-05 06:22:21 +00:00
|
|
|
else{
|
2012-07-31 06:15:24 +00:00
|
|
|
// Don't automatically transition to RINGIN, wait for a client to tell us when.
|
2012-07-05 06:22:21 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (VOMP_STATE_CALLPREP<<3)|VOMP_STATE_NOCALL:
|
|
|
|
case (VOMP_STATE_CALLPREP<<3)|VOMP_STATE_CALLPREP:
|
|
|
|
/* We are getting ready to ring, and the other end has issued a session
|
|
|
|
number, (and may be calling us at the same time).
|
|
|
|
Now is the time to ring out.
|
|
|
|
However, until the remote party has acknowledged with RINGIN,
|
|
|
|
don't indicate their ringing state to the user.
|
|
|
|
*/
|
|
|
|
if (call->initiated_call){
|
|
|
|
// TODO fail the call if we can't agree on codec's
|
2012-08-08 01:26:05 +00:00
|
|
|
recvr_state=VOMP_STATE_RINGINGOUT;
|
2012-07-05 06:22:21 +00:00
|
|
|
}else{
|
2012-08-08 01:26:05 +00:00
|
|
|
recvr_state=VOMP_STATE_CALLENDED;
|
2012-04-18 21:44:51 +00:00
|
|
|
}
|
2012-07-05 06:22:21 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case (VOMP_STATE_RINGINGOUT<<3)|VOMP_STATE_NOCALL:
|
|
|
|
case (VOMP_STATE_RINGINGOUT<<3)|VOMP_STATE_CALLPREP:
|
|
|
|
/* We are calling them, and they have not yet answered, just wait */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (VOMP_STATE_RINGINGOUT<<3)|VOMP_STATE_RINGINGIN:
|
|
|
|
/* we are calling them and they have acknowledged it.
|
|
|
|
Now we can play a tone to indicate they are ringing */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (VOMP_STATE_RINGINGOUT<<3)|VOMP_STATE_RINGINGOUT:
|
|
|
|
/* Woah, we're trying to dial each other?? That must have been well timed.
|
|
|
|
Jump to INCALL and start audio */
|
2012-08-08 01:26:05 +00:00
|
|
|
recvr_state=VOMP_STATE_INCALL;
|
2012-07-05 06:22:21 +00:00
|
|
|
// reset create time when call is established
|
2012-07-30 07:52:38 +00:00
|
|
|
call->create_time=gettime_ms();
|
2012-07-05 06:22:21 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case (VOMP_STATE_INCALL<<3)|VOMP_STATE_RINGINGOUT:
|
|
|
|
/* we think the call is in progress, but the far end hasn't replied yet
|
|
|
|
Just wait. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (VOMP_STATE_RINGINGOUT<<3)|VOMP_STATE_INCALL:
|
|
|
|
/* They have answered, we can jump to incall as well */
|
2012-08-08 01:26:05 +00:00
|
|
|
recvr_state=VOMP_STATE_INCALL;
|
2012-07-05 06:22:21 +00:00
|
|
|
// reset create time when call is established
|
2012-07-30 07:52:38 +00:00
|
|
|
call->create_time=gettime_ms();
|
2012-07-05 06:22:21 +00:00
|
|
|
// Fall through
|
|
|
|
case (VOMP_STATE_INCALL<<3)|VOMP_STATE_INCALL:
|
|
|
|
/* play any audio that they have sent us. */
|
|
|
|
if (!call->audio_started) {
|
|
|
|
if (vomp_call_start_audio(call)) call->local.codec=VOMP_CODEC_ENGAGED;
|
|
|
|
}
|
|
|
|
vomp_process_audio(call,sender_duration,mdp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (VOMP_STATE_CALLENDED<<3)|VOMP_STATE_NOCALL:
|
|
|
|
case (VOMP_STATE_CALLENDED<<3)|VOMP_STATE_CALLPREP:
|
|
|
|
case (VOMP_STATE_CALLENDED<<3)|VOMP_STATE_RINGINGOUT:
|
|
|
|
case (VOMP_STATE_CALLENDED<<3)|VOMP_STATE_RINGINGIN:
|
|
|
|
case (VOMP_STATE_CALLENDED<<3)|VOMP_STATE_INCALL:
|
|
|
|
case (VOMP_STATE_CALLENDED<<3)|VOMP_STATE_CALLENDED:
|
|
|
|
/* If we ended the call, we'll wait for the far end to reply before destroying it */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
Any state not explicitly listed above is considered invalid and possibly stale,
|
|
|
|
the packet will be completely ignored.
|
|
|
|
*/
|
2012-08-08 01:26:05 +00:00
|
|
|
WHYF("Ignoring invalid call state %d.%d",sender_state,recvr_state);
|
2012-07-05 06:22:21 +00:00
|
|
|
return 0;
|
2012-04-18 21:44:51 +00:00
|
|
|
}
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
call->remote.sequence=sender_seq;
|
2012-08-08 01:26:05 +00:00
|
|
|
|
|
|
|
vomp_update_remote_state(call, sender_state);
|
|
|
|
vomp_update_local_state(call, recvr_state);
|
2012-07-30 07:52:38 +00:00
|
|
|
call->last_activity=gettime_ms();
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
// TODO if we hear a stale echo of our state should we force another outgoing packet now?
|
|
|
|
// will that always cause 2 outgoing packets?
|
|
|
|
|
|
|
|
/* send an update to the call status if required */
|
|
|
|
vomp_update(call);
|
|
|
|
|
2012-08-08 01:26:05 +00:00
|
|
|
if (sender_state==VOMP_STATE_CALLENDED
|
|
|
|
&&recvr_state==VOMP_STATE_CALLENDED)
|
2012-07-05 06:22:21 +00:00
|
|
|
return vomp_call_destroy(call);
|
2012-04-18 21:44:51 +00:00
|
|
|
}
|
2012-04-21 21:38:33 +00:00
|
|
|
return 0;
|
2012-04-18 21:44:51 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* unsupported VoMP frame */
|
2012-04-21 21:38:33 +00:00
|
|
|
WHYF("Unsupported VoMP frame type = 0x%02x",mdp->in.payload[0]);
|
2012-04-18 21:44:51 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-04-18 23:17:59 +00:00
|
|
|
return WHY("Malformed VoMP MDP packet?");
|
2012-04-18 21:44:51 +00:00
|
|
|
}
|
2012-04-20 06:11:13 +00:00
|
|
|
|
|
|
|
char *vomp_describe_state(int state)
|
|
|
|
{
|
|
|
|
switch(state) {
|
|
|
|
case VOMP_STATE_CALLENDED: return "CALLENDED";
|
|
|
|
case VOMP_STATE_INCALL: return "INCALL";
|
|
|
|
case VOMP_STATE_RINGINGIN: return "RINGINGIN";
|
|
|
|
case VOMP_STATE_RINGINGOUT: return "RINGINGOUT";
|
|
|
|
case VOMP_STATE_CALLPREP: return "CALLPREP";
|
|
|
|
case VOMP_STATE_NOCALL: return "NOCALL";
|
|
|
|
}
|
|
|
|
return "UNKNOWN";
|
|
|
|
}
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
static int dump_vomp_status()
|
2012-04-20 23:37:36 +00:00
|
|
|
{
|
2012-04-20 06:11:13 +00:00
|
|
|
int i;
|
2012-08-02 01:59:37 +00:00
|
|
|
DEBUGF(">>> Active VoMP call states:");
|
2012-04-20 06:11:13 +00:00
|
|
|
for(i=0;i<vomp_call_count;i++)
|
|
|
|
{
|
2012-08-02 01:59:37 +00:00
|
|
|
DEBUGF("%s/%06x -> %s/%06x (%s -> %s)",
|
2012-07-03 08:29:30 +00:00
|
|
|
alloca_tohex_sid(vomp_call_states[i].local.sid),
|
2012-04-20 23:37:36 +00:00
|
|
|
vomp_call_states[i].local.session,
|
2012-07-03 08:29:30 +00:00
|
|
|
alloca_tohex_sid(vomp_call_states[i].remote.sid),
|
2012-04-20 23:37:36 +00:00
|
|
|
vomp_call_states[i].remote.session,
|
2012-04-20 06:11:13 +00:00
|
|
|
vomp_call_states[i].local.did,
|
|
|
|
vomp_call_states[i].remote.did);
|
2012-08-02 01:59:37 +00:00
|
|
|
DEBUGF(" local state=%s, remote state=%s",
|
2012-04-20 06:11:13 +00:00
|
|
|
vomp_describe_state(vomp_call_states[i].local.state),
|
|
|
|
vomp_describe_state(vomp_call_states[i].remote.state));
|
|
|
|
}
|
2012-08-02 01:59:37 +00:00
|
|
|
if (!vomp_call_count) DEBUGF("No active calls");
|
2012-04-20 06:11:13 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
static const char *vomp_describe_codec(int c)
|
2012-04-23 04:25:17 +00:00
|
|
|
{
|
|
|
|
switch(c) {
|
|
|
|
case VOMP_CODEC_NONE: return "none";
|
|
|
|
case VOMP_CODEC_CODEC2_2400: return "CODEC2@1400";
|
|
|
|
case VOMP_CODEC_CODEC2_1400: return "CODEC2@2400";
|
|
|
|
case VOMP_CODEC_GSMHALF: return "GSM-half-rate";
|
|
|
|
case VOMP_CODEC_GSMFULL: return "GSM-full-rate";
|
|
|
|
case VOMP_CODEC_16SIGNED: return "16bit-raw";
|
|
|
|
case VOMP_CODEC_8ULAW: return "8bit-uLaw";
|
|
|
|
case VOMP_CODEC_8ALAW: return "8bit-aLaw";
|
2012-05-06 12:26:40 +00:00
|
|
|
case VOMP_CODEC_PCM: return "PCM@8KHz";
|
2012-04-23 04:25:17 +00:00
|
|
|
case VOMP_CODEC_DTMF: return "DTMF";
|
|
|
|
case VOMP_CODEC_ENGAGED: return "Engaged-tone";
|
|
|
|
case VOMP_CODEC_ONHOLD: return "On-Hold";
|
|
|
|
case VOMP_CODEC_CALLERID: return "CallerID";
|
|
|
|
}
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
strbuf strbuf_append_vomp_supported_codecs(strbuf sb, const unsigned char supported_codecs[256])
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
const char *p = strbuf_end(sb);
|
|
|
|
for (i = 0; i < 256; ++i)
|
|
|
|
if (supported_codecs[i]) {
|
|
|
|
if (p != strbuf_end(sb))
|
|
|
|
strbuf_putc(sb, ' ');
|
|
|
|
strbuf_puts(sb, vomp_describe_codec(i));
|
|
|
|
}
|
|
|
|
return sb;
|
|
|
|
}
|
|
|
|
|
2012-04-24 05:57:20 +00:00
|
|
|
int vomp_sample_size(int c)
|
|
|
|
{
|
|
|
|
switch(c) {
|
|
|
|
case VOMP_CODEC_NONE: return 0;
|
|
|
|
case VOMP_CODEC_CODEC2_2400: return 7; /* actually 2550bps, 51 bits per 20ms,
|
|
|
|
but using whole byte here, so 2800bps */
|
|
|
|
case VOMP_CODEC_CODEC2_1400: return 7; /* per 40ms */
|
|
|
|
case VOMP_CODEC_GSMHALF: return 14; /* check. 5.6kbits */
|
|
|
|
case VOMP_CODEC_GSMFULL: return 33; /* padded to 13.2kbit/sec */
|
|
|
|
case VOMP_CODEC_16SIGNED: return 320; /* 8000x2bytes*0.02sec */
|
|
|
|
case VOMP_CODEC_8ULAW: return 160;
|
|
|
|
case VOMP_CODEC_8ALAW: return 160;
|
2012-05-06 11:42:31 +00:00
|
|
|
case VOMP_CODEC_PCM: return 320;
|
2012-04-24 05:57:20 +00:00
|
|
|
case VOMP_CODEC_DTMF: return 1;
|
|
|
|
case VOMP_CODEC_ENGAGED: return 0;
|
|
|
|
case VOMP_CODEC_ONHOLD: return 0;
|
|
|
|
case VOMP_CODEC_CALLERID: return 32;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-04-24 10:56:16 +00:00
|
|
|
int vomp_codec_timespan(int c)
|
|
|
|
{
|
|
|
|
switch(c) {
|
|
|
|
case VOMP_CODEC_NONE: return 1;
|
|
|
|
case VOMP_CODEC_CODEC2_2400: return 20;
|
|
|
|
case VOMP_CODEC_CODEC2_1400: return 40;
|
|
|
|
case VOMP_CODEC_GSMHALF: return 20;
|
|
|
|
case VOMP_CODEC_GSMFULL: return 20;
|
|
|
|
case VOMP_CODEC_16SIGNED: return 20;
|
|
|
|
case VOMP_CODEC_8ULAW: return 20;
|
|
|
|
case VOMP_CODEC_8ALAW: return 20;
|
2012-05-06 11:42:31 +00:00
|
|
|
case VOMP_CODEC_PCM: return 20;
|
2012-04-24 20:08:45 +00:00
|
|
|
case VOMP_CODEC_DTMF: return 80;
|
2012-04-24 10:56:16 +00:00
|
|
|
case VOMP_CODEC_ENGAGED: return 20;
|
|
|
|
case VOMP_CODEC_ONHOLD: return 20;
|
|
|
|
case VOMP_CODEC_CALLERID: return 0;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-23 07:42:10 +00:00
|
|
|
int app_vomp_status(int argc, const char *const *argv, struct command_line_option *o)
|
2012-04-20 23:37:36 +00:00
|
|
|
{
|
2012-04-21 23:01:19 +00:00
|
|
|
overlay_mdp_frame mdp;
|
|
|
|
bzero(&mdp,sizeof(mdp));
|
|
|
|
|
|
|
|
mdp.packetTypeAndFlags=MDP_VOMPEVENT;
|
|
|
|
mdp.vompevent.flags=VOMPEVENT_CALLINFO;
|
|
|
|
if (overlay_mdp_send(&mdp,MDP_AWAITREPLY,5000))
|
|
|
|
{
|
|
|
|
WHY("Current call state information request failed.");
|
|
|
|
if (mdp.packetTypeAndFlags==MDP_ERROR&&mdp.error.error)
|
2012-08-02 01:59:37 +00:00
|
|
|
WHYF("MDP: error=%d, message='%s'",
|
2012-04-21 23:01:19 +00:00
|
|
|
mdp.error.error,mdp.error.message);
|
2012-04-22 09:17:24 +00:00
|
|
|
overlay_mdp_client_done();
|
2012-04-21 23:01:19 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2012-04-21 23:18:23 +00:00
|
|
|
if (mdp.packetTypeAndFlags!=MDP_VOMPEVENT) {
|
2012-08-02 01:59:37 +00:00
|
|
|
WHYF("Received incorrect reply type from server (received MDP message type 0x%04x)",mdp.packetTypeAndFlags);
|
2012-04-22 09:17:24 +00:00
|
|
|
overlay_mdp_client_done();
|
|
|
|
return -1;
|
2012-04-21 23:18:23 +00:00
|
|
|
}
|
|
|
|
if (mdp.vompevent.flags!=VOMPEVENT_CALLINFO) {
|
2012-08-02 01:59:37 +00:00
|
|
|
WHYF("Received incorrect reply type from server (received VoMP message type 0x%04x)",mdp.vompevent.flags);
|
2012-04-22 09:17:24 +00:00
|
|
|
overlay_mdp_client_done();
|
|
|
|
return -1;
|
2012-04-21 23:18:23 +00:00
|
|
|
}
|
2012-04-21 23:01:19 +00:00
|
|
|
int i;
|
2012-04-21 23:18:23 +00:00
|
|
|
int count=0;
|
|
|
|
overlay_mdp_frame mdp2;
|
|
|
|
bzero(&mdp2,sizeof(mdp2));
|
2012-04-21 23:01:19 +00:00
|
|
|
for(i=0;i<VOMP_MAX_CALLS;i++)
|
2012-04-21 23:18:23 +00:00
|
|
|
if (mdp.vompevent.other_calls_sessions[i])
|
|
|
|
{
|
|
|
|
count++;
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP) {
|
|
|
|
strbuf b = strbuf_alloca(1024);
|
|
|
|
strbuf_sprintf(b, "%06x:%s:",
|
2012-04-21 23:18:23 +00:00
|
|
|
mdp.vompevent.other_calls_sessions[i],
|
|
|
|
vomp_describe_state(mdp.vompevent.other_calls_states[i]));
|
2012-08-02 01:59:37 +00:00
|
|
|
mdp2.packetTypeAndFlags=MDP_VOMPEVENT;
|
|
|
|
mdp2.vompevent.flags=VOMPEVENT_CALLINFO;
|
|
|
|
mdp2.vompevent.call_session_token=mdp.vompevent.other_calls_sessions[i];
|
|
|
|
if (overlay_mdp_send(&mdp2,MDP_AWAITREPLY,5000))
|
|
|
|
strbuf_puts(b, "<server failed to provide detail>");
|
|
|
|
else {
|
2012-04-21 23:18:23 +00:00
|
|
|
if (mdp2.vompevent.call_session_token!=mdp.vompevent.other_calls_sessions[i])
|
2012-08-02 01:59:37 +00:00
|
|
|
strbuf_sprintf(b, "<strange reply from server (%04x, %04x, token %06x)>",
|
2012-04-21 23:18:23 +00:00
|
|
|
mdp.packetTypeAndFlags,mdp.vompevent.flags,
|
|
|
|
mdp2.vompevent.call_session_token);
|
|
|
|
else {
|
2012-08-02 01:59:37 +00:00
|
|
|
strbuf_sprintf(b, "%s* -> %s* (%s -> %s)",
|
2012-07-04 01:00:46 +00:00
|
|
|
alloca_tohex(mdp2.vompevent.local_sid, 6),
|
|
|
|
alloca_tohex(mdp2.vompevent.remote_sid, 6),
|
2012-04-21 23:18:23 +00:00
|
|
|
strlen(mdp2.vompevent.local_did)
|
|
|
|
?mdp2.vompevent.local_did:"<no local number>",
|
|
|
|
strlen(mdp2.vompevent.remote_did)
|
|
|
|
?mdp2.vompevent.remote_did:"<no remote number>");
|
|
|
|
}
|
2012-08-02 01:59:37 +00:00
|
|
|
strbuf_puts(b, " supports ");
|
|
|
|
strbuf_append_vomp_supported_codecs(b, mdp2.vompevent.supported_codecs);
|
2012-04-21 23:18:23 +00:00
|
|
|
}
|
2012-08-02 01:59:37 +00:00
|
|
|
DEBUG(strbuf_str(b));
|
|
|
|
}
|
2012-04-21 23:18:23 +00:00
|
|
|
}
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("%d live call descriptors.",count);
|
2012-04-22 09:17:24 +00:00
|
|
|
return overlay_mdp_client_done();
|
2012-04-20 23:37:36 +00:00
|
|
|
}
|
|
|
|
|
2012-04-23 07:42:10 +00:00
|
|
|
int app_vomp_dial(int argc, const char *const *argv, struct command_line_option *o)
|
2012-04-20 06:11:13 +00:00
|
|
|
{
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
|
2012-04-23 08:55:26 +00:00
|
|
|
const char *sid,*did,*callerid;
|
2012-04-20 06:11:13 +00:00
|
|
|
cli_arg(argc, argv, o, "sid", &sid, NULL, "");
|
|
|
|
cli_arg(argc, argv, o, "did", &did, NULL, "");
|
|
|
|
cli_arg(argc, argv, o, "callerid", &callerid, NULL, NULL);
|
|
|
|
|
|
|
|
overlay_mdp_frame mdp;
|
|
|
|
bzero(&mdp,sizeof(mdp));
|
|
|
|
|
|
|
|
mdp.packetTypeAndFlags=MDP_VOMPEVENT;
|
|
|
|
mdp.vompevent.flags=VOMPEVENT_DIAL;
|
|
|
|
if (overlay_mdp_getmyaddr(0,&mdp.vompevent.local_sid[0])) return -1;
|
2012-04-20 22:45:48 +00:00
|
|
|
stowSid(&mdp.vompevent.remote_sid[0],0,sid);
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP) {
|
|
|
|
DEBUGF("local_sid=%s",alloca_tohex_sid(mdp.vompevent.local_sid));
|
|
|
|
DEBUGF("remote_sid=%s from %s", alloca_tohex_sid(mdp.vompevent.remote_sid),sid);
|
|
|
|
}
|
2012-04-20 06:11:13 +00:00
|
|
|
if (overlay_mdp_send(&mdp,MDP_AWAITREPLY,5000))
|
2012-08-02 01:59:37 +00:00
|
|
|
WHY("Dial request failed.");
|
2012-04-21 21:46:18 +00:00
|
|
|
if (mdp.packetTypeAndFlags==MDP_ERROR&&mdp.error.error)
|
2012-08-02 01:59:37 +00:00
|
|
|
WHYF("Dial request failed: error=%d, message='%s'", mdp.error.error,mdp.error.message);
|
|
|
|
else {
|
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("Dial request accepted.");
|
|
|
|
}
|
2012-04-22 09:17:24 +00:00
|
|
|
return overlay_mdp_client_done();
|
2012-08-02 01:59:37 +00:00
|
|
|
}
|
2012-04-20 06:11:13 +00:00
|
|
|
|
2012-04-24 01:34:29 +00:00
|
|
|
int vomp_parse_dtmf_digit(char c)
|
|
|
|
{
|
|
|
|
if (c>='0'&&c<='9') return c-0x30;
|
|
|
|
switch (c) {
|
|
|
|
case 'a': case 'A': return 0xa;
|
|
|
|
case 'b': case 'B': return 0xb;
|
|
|
|
case 'c': case 'C': return 0xc;
|
|
|
|
case 'd': case 'D': return 0xd;
|
|
|
|
case '*': return 0xe;
|
|
|
|
case '#': return 0xf;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char vomp_dtmf_digit_to_char(int digit)
|
|
|
|
{
|
|
|
|
if (digit<0) return '?';
|
|
|
|
if (digit<10) return '0'+digit;
|
|
|
|
if (digit<0xe) return 'A'+digit-0xa;
|
|
|
|
if (digit==0xe) return '*';
|
|
|
|
if (digit==0xf) return '#';
|
|
|
|
return '?';
|
|
|
|
}
|
|
|
|
|
2012-04-24 02:44:53 +00:00
|
|
|
int app_vomp_dtmf(int argc, const char *const *argv, struct command_line_option *o)
|
2012-04-24 01:34:29 +00:00
|
|
|
{
|
2012-04-24 02:44:53 +00:00
|
|
|
const char *call_token;
|
|
|
|
const char *digits;
|
2012-04-24 01:34:29 +00:00
|
|
|
cli_arg(argc, argv, o, "call", &call_token, NULL, "");
|
|
|
|
cli_arg(argc, argv, o, "digits", &digits,NULL,"");
|
|
|
|
|
|
|
|
overlay_mdp_frame mdp;
|
|
|
|
bzero(&mdp,sizeof(mdp));
|
|
|
|
|
|
|
|
mdp.packetTypeAndFlags=MDP_VOMPEVENT;
|
|
|
|
mdp.vompevent.flags=VOMPEVENT_AUDIOPACKET;
|
|
|
|
mdp.vompevent.call_session_token=strtol(call_token,NULL,16);
|
|
|
|
|
2012-04-24 05:57:20 +00:00
|
|
|
/* One digit per sample block. */
|
2012-04-24 01:34:29 +00:00
|
|
|
mdp.vompevent.audio_sample_codec=VOMP_CODEC_DTMF;
|
2012-04-24 09:03:40 +00:00
|
|
|
mdp.vompevent.audio_sample_bytes=1;
|
|
|
|
|
2012-04-24 01:34:29 +00:00
|
|
|
int i;
|
|
|
|
for(i=0;i<strlen(digits);i++) {
|
|
|
|
int digit=vomp_parse_dtmf_digit(digits[i]);
|
2012-08-02 01:59:37 +00:00
|
|
|
if (digit<0)
|
|
|
|
return WHYF("'%c' is not a DTMF digit.",digits[i]);
|
2012-04-24 05:57:20 +00:00
|
|
|
mdp.vompevent.audio_bytes[mdp.vompevent.audio_sample_bytes]
|
2012-04-24 20:08:45 +00:00
|
|
|
=(digit<<4); /* 80ms standard tone duration, so that it is a multiple
|
|
|
|
of the majority of codec time units (70ms is the nominal
|
|
|
|
DTMF tone length for most systems). */
|
2012-08-02 01:59:37 +00:00
|
|
|
if (overlay_mdp_send(&mdp,0,0))
|
|
|
|
WHY("Send DTMF failed");
|
2012-04-24 01:34:29 +00:00
|
|
|
}
|
2012-08-02 01:59:37 +00:00
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("DTMF digit(s) sent.");
|
2012-04-24 01:34:29 +00:00
|
|
|
return overlay_mdp_client_done();
|
2012-08-02 01:59:37 +00:00
|
|
|
}
|
2012-04-24 01:34:29 +00:00
|
|
|
|
2012-04-22 03:21:58 +00:00
|
|
|
|
2012-04-23 07:42:10 +00:00
|
|
|
int app_vomp_pickup(int argc, const char *const *argv, struct command_line_option *o)
|
2012-04-22 03:21:58 +00:00
|
|
|
{
|
2012-04-23 08:55:26 +00:00
|
|
|
const char *call_token;
|
2012-04-22 03:21:58 +00:00
|
|
|
cli_arg(argc, argv, o, "call", &call_token, NULL, "");
|
|
|
|
|
|
|
|
overlay_mdp_frame mdp;
|
|
|
|
bzero(&mdp,sizeof(mdp));
|
|
|
|
|
|
|
|
mdp.packetTypeAndFlags=MDP_VOMPEVENT;
|
|
|
|
mdp.vompevent.flags=VOMPEVENT_PICKUP;
|
|
|
|
mdp.vompevent.call_session_token=strtol(call_token,NULL,16);
|
|
|
|
|
|
|
|
if (overlay_mdp_send(&mdp,MDP_AWAITREPLY,5000))
|
2012-08-02 01:59:37 +00:00
|
|
|
WHY("Pickup request failed.");
|
2012-04-22 03:21:58 +00:00
|
|
|
if (mdp.packetTypeAndFlags==MDP_ERROR&&mdp.error.error)
|
2012-08-02 01:59:37 +00:00
|
|
|
WHYF("Pickup request failed: error=%d, message='%s'", mdp.error.error,mdp.error.message);
|
|
|
|
else {
|
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("Pickup request accepted.");
|
|
|
|
}
|
2012-04-22 09:17:24 +00:00
|
|
|
return overlay_mdp_client_done();
|
2012-08-02 01:59:37 +00:00
|
|
|
}
|
2012-04-22 03:21:58 +00:00
|
|
|
|
2012-04-23 07:42:10 +00:00
|
|
|
int app_vomp_hangup(int argc, const char *const *argv, struct command_line_option *o)
|
2012-04-22 03:21:58 +00:00
|
|
|
{
|
2012-04-23 08:55:26 +00:00
|
|
|
const char *call_token;
|
2012-04-22 03:21:58 +00:00
|
|
|
cli_arg(argc, argv, o, "call", &call_token, NULL, "");
|
|
|
|
|
|
|
|
overlay_mdp_frame mdp;
|
|
|
|
bzero(&mdp,sizeof(mdp));
|
|
|
|
|
|
|
|
mdp.packetTypeAndFlags=MDP_VOMPEVENT;
|
|
|
|
mdp.vompevent.flags=VOMPEVENT_HANGUP;
|
|
|
|
mdp.vompevent.call_session_token=strtol(call_token,NULL,16);
|
|
|
|
|
|
|
|
if (overlay_mdp_send(&mdp,MDP_AWAITREPLY,5000))
|
|
|
|
{
|
|
|
|
WHY("Hangup/reject request failed.");
|
|
|
|
}
|
|
|
|
if (mdp.packetTypeAndFlags==MDP_ERROR&&mdp.error.error)
|
2012-08-02 01:59:37 +00:00
|
|
|
WHYF("Hangup/reject request failed: error=%d, message='%s'", mdp.error.error,mdp.error.message);
|
|
|
|
else {
|
|
|
|
if (debug & DEBUG_VOMP)
|
|
|
|
DEBUGF("Hangup/reject request accepted.");
|
|
|
|
}
|
2012-04-22 09:17:24 +00:00
|
|
|
return overlay_mdp_client_done();
|
2012-08-02 01:59:37 +00:00
|
|
|
}
|
2012-04-22 03:21:58 +00:00
|
|
|
|
2012-04-23 07:42:10 +00:00
|
|
|
int app_vomp_monitor(int argc, const char *const *argv, struct command_line_option *o)
|
2012-04-22 09:17:24 +00:00
|
|
|
{
|
|
|
|
overlay_mdp_frame mdp;
|
|
|
|
bzero(&mdp,sizeof(mdp));
|
|
|
|
|
|
|
|
mdp.packetTypeAndFlags=MDP_VOMPEVENT;
|
|
|
|
mdp.vompevent.flags=VOMPEVENT_REGISTERINTEREST;
|
2012-04-23 04:25:17 +00:00
|
|
|
mdp.vompevent.supported_codecs[0]=VOMP_CODEC_DTMF;
|
|
|
|
mdp.vompevent.supported_codecs[1]=VOMP_CODEC_NONE;
|
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
if (overlay_mdp_send(&mdp,MDP_AWAITREPLY,5000)) {
|
|
|
|
WHY("Failed to register interest in telephony events.");
|
|
|
|
overlay_mdp_client_done();
|
|
|
|
if (mdp.packetTypeAndFlags==MDP_ERROR&&mdp.error.error)
|
|
|
|
WHYF(" MDP Server error #%d: '%s'", mdp.error.error,mdp.error.message);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-04-22 09:17:24 +00:00
|
|
|
|
|
|
|
while(!servalShutdown) {
|
|
|
|
overlay_mdp_frame rx;
|
|
|
|
int ttl;
|
2012-04-22 10:47:32 +00:00
|
|
|
/* In theory we should be able to ask for a timeout of -1 for
|
|
|
|
infinity, but broken poll() and select() implementations on OSX
|
|
|
|
make this impossible. So one unnecessary check per second is
|
|
|
|
probably tolerable. */
|
2012-08-02 01:59:37 +00:00
|
|
|
if (overlay_mdp_client_poll(1000) > 0 && !overlay_mdp_recv(&rx,&ttl)) {
|
|
|
|
switch(rx.packetTypeAndFlags) {
|
|
|
|
case MDP_ERROR:
|
|
|
|
WHYF("MDP Server error #%d: '%s'", rx.error.error,rx.error.message);
|
|
|
|
break;
|
|
|
|
case MDP_VOMPEVENT: {
|
|
|
|
strbuf b = strbuf_alloca(1024);
|
|
|
|
strbuf_sprintf(b, "VoMP call descriptor %06x %s:%s",
|
|
|
|
rx.vompevent.call_session_token,
|
|
|
|
vomp_describe_state(rx.vompevent.local_state),
|
|
|
|
vomp_describe_state(rx.vompevent.remote_state));
|
|
|
|
if (rx.vompevent.flags&VOMPEVENT_RINGING)
|
|
|
|
strbuf_puts(b, " RINGING");
|
|
|
|
if (rx.vompevent.flags&VOMPEVENT_CALLENDED)
|
|
|
|
strbuf_puts(b, " CALLENDED");
|
|
|
|
if (rx.vompevent.flags&VOMPEVENT_CALLREJECT)
|
|
|
|
strbuf_puts(b, " CALLREJECTED");
|
|
|
|
if (rx.vompevent.flags&VOMPEVENT_CALLCREATED)
|
|
|
|
strbuf_puts(b, " CREATED");
|
|
|
|
if (rx.vompevent.flags&VOMPEVENT_AUDIOSTREAMING)
|
|
|
|
strbuf_puts(b, " AUDIOSTREAMING");
|
|
|
|
strbuf_puts(b, " codecs: ");
|
|
|
|
strbuf_append_vomp_supported_codecs(b, rx.vompevent.supported_codecs);
|
|
|
|
DEBUG(strbuf_str(b));
|
|
|
|
if (rx.vompevent.audio_sample_codec) {
|
|
|
|
DEBUGF(" attached audio sample: codec=%s, len=%d",
|
|
|
|
vomp_describe_codec(rx.vompevent.audio_sample_codec),
|
|
|
|
rx.vompevent.audio_sample_bytes);
|
2012-08-09 02:44:32 +00:00
|
|
|
DEBUGF(" sample covers %ldms - %ldms of call.",
|
|
|
|
(long) rx.vompevent.audio_sample_starttime,
|
|
|
|
(long) rx.vompevent.audio_sample_endtime);
|
2012-04-22 09:30:36 +00:00
|
|
|
}
|
|
|
|
}
|
2012-08-02 01:59:37 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
DEBUGF("Unknown message (type=0x%02x)",rx.packetTypeAndFlags);
|
|
|
|
}
|
|
|
|
}
|
2012-04-22 09:17:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mdp.packetTypeAndFlags=MDP_VOMPEVENT;
|
|
|
|
mdp.vompevent.flags=VOMPEVENT_WITHDRAWINTEREST;
|
2012-08-02 01:59:37 +00:00
|
|
|
if (overlay_mdp_send(&mdp,MDP_AWAITREPLY,5000)) {
|
|
|
|
WHY("Failed to deregister interest in telephony events.");
|
|
|
|
overlay_mdp_client_done();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (mdp.packetTypeAndFlags==MDP_ERROR&&mdp.error.error)
|
|
|
|
DEBUGF("MDP Server error #%d: '%s'", mdp.error.error,mdp.error.message);
|
2012-04-22 09:17:24 +00:00
|
|
|
return overlay_mdp_client_done();
|
|
|
|
}
|
2012-04-22 03:21:58 +00:00
|
|
|
|
2012-08-02 01:59:37 +00:00
|
|
|
static void vomp_process_tick(struct sched_ent *alarm)
|
|
|
|
{
|
2012-07-03 05:43:39 +00:00
|
|
|
char msg[32];
|
|
|
|
int len;
|
2012-08-09 02:44:32 +00:00
|
|
|
time_ms_t now = gettime_ms();
|
2012-07-03 05:43:39 +00:00
|
|
|
|
2012-08-06 05:16:46 +00:00
|
|
|
struct vomp_call_state *call = (struct vomp_call_state *)alarm;
|
2012-07-03 05:43:39 +00:00
|
|
|
|
2012-08-09 02:22:44 +00:00
|
|
|
/* See if any calls need to be expired.
|
|
|
|
Allow VOMP_CALL_DIAL_TIMEOUT ms for the other party to ring / request ringing
|
|
|
|
Allow VOMP_CALL_RING_TIMEOUT ms for the ringing party to answer
|
|
|
|
Allow VOMP_CALL_NETWORK_TIMEOUT ms between received packets
|
|
|
|
*/
|
|
|
|
|
|
|
|
if ((call->remote.state < VOMP_STATE_RINGINGOUT && call->create_time + VOMP_CALL_DIAL_TIMEOUT < now) ||
|
|
|
|
(call->local.state < VOMP_STATE_INCALL && call->create_time + VOMP_CALL_RING_TIMEOUT < now) ||
|
|
|
|
(call->last_activity+VOMP_CALL_NETWORK_TIMEOUT<now) ){
|
2012-08-08 01:26:05 +00:00
|
|
|
vomp_call_destroy(call);
|
|
|
|
return;
|
2012-07-03 05:43:39 +00:00
|
|
|
}
|
2012-07-05 06:22:21 +00:00
|
|
|
|
|
|
|
/* update everyone if the state has changed */
|
|
|
|
vomp_update(call);
|
|
|
|
/* force a packet to the other party. We are still here */
|
|
|
|
vomp_send_status_remote(call);
|
|
|
|
|
|
|
|
/* tell local monitor clients the call is still alive */
|
|
|
|
len = snprintf(msg,sizeof(msg) -1,"\nKEEPALIVE:%06x\n", call->local.session);
|
|
|
|
monitor_tell_clients(msg, len, MONITOR_VOMP);
|
|
|
|
|
2012-07-30 07:52:38 +00:00
|
|
|
alarm->alarm = gettime_ms() + VOMP_CALL_STATUS_INTERVAL;
|
2012-07-12 00:45:16 +00:00
|
|
|
alarm->deadline = alarm->alarm + VOMP_CALL_STATUS_INTERVAL/2;
|
2012-07-02 03:49:54 +00:00
|
|
|
schedule(alarm);
|
2012-04-21 11:51:26 +00:00
|
|
|
}
|