mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-01-29 15:43:56 +00:00
Shrink vomp headers, reduce servald's knowledge of codec details
This commit is contained in:
parent
383cc2371d
commit
a4eaf37bbd
52
codecs.c
52
codecs.c
@ -1,52 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2012 Paul Gardner-Stephen
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "serval.h"
|
||||
|
||||
int encodeAndDispatchRecordedAudio(int fd,int callSessionToken,
|
||||
int recordCodec,
|
||||
unsigned char *sampleData,
|
||||
int sampleBytes)
|
||||
{
|
||||
switch (recordCodec) {
|
||||
case VOMP_CODEC_PCM:
|
||||
/* leave data raw, so need to rewrite sampleData or sampleBytes */
|
||||
break;
|
||||
default:
|
||||
WHYF("Codec not yet supported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char msg[128+MAX_AUDIO_BYTES];
|
||||
snprintf(msg,128,"\n*%d:AUDIO:%x:%d\n",sampleBytes,callSessionToken,recordCodec);
|
||||
int len=strlen(msg);
|
||||
bcopy(&sampleData[0],&msg[len],sampleBytes);
|
||||
len+=sampleBytes;
|
||||
write(fd,msg,len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bufferAudioForPlayback(int codec, time_ms_t start_time, time_ms_t end_time, unsigned char *data,int dataLen)
|
||||
{
|
||||
/* XXX We need to buffer and reorder out-of-order sample blocks and
|
||||
decode codecs etc here. */
|
||||
|
||||
/* send audio to device */
|
||||
// int bytesWritten=audev->write(&data[0],dataLen);
|
||||
return 0;
|
||||
}
|
23
constants.h
23
constants.h
@ -171,18 +171,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
frame of audio (preemptive audio retransmission) */
|
||||
#define VOMP_MAX_RECENT_SAMPLES 2
|
||||
|
||||
#define VOMP_CODEC_NONE 0x00
|
||||
#define VOMP_CODEC_CODEC2_2400 0x01
|
||||
#define VOMP_CODEC_CODEC2_1400 0x02
|
||||
#define VOMP_CODEC_GSMHALF 0x03
|
||||
#define VOMP_CODEC_GSMFULL 0x04
|
||||
#define VOMP_CODEC_16SIGNED 0x05
|
||||
#define VOMP_CODEC_8ULAW 0x06
|
||||
#define VOMP_CODEC_8ALAW 0x07
|
||||
#define VOMP_CODEC_PCM 0x08
|
||||
#define VOMP_CODEC_DTMF 0x80
|
||||
#define VOMP_CODEC_ENGAGED 0x81
|
||||
#define VOMP_CODEC_ONHOLD 0x82
|
||||
// codec's with well defined parameters
|
||||
#define VOMP_CODEC_16SIGNED 0x01
|
||||
#define VOMP_CODEC_ULAW 0x02
|
||||
#define VOMP_CODEC_ALAW 0x03
|
||||
#define VOMP_CODEC_GSM 0x04
|
||||
|
||||
// other out of band signals, probably shouldn't be codecs
|
||||
#define VOMP_CODEC_DTMF 0x20
|
||||
#define VOMP_CODEC_TEXT 0x21
|
||||
|
||||
// Note, Don't add codec's we aren't using yet
|
||||
|
||||
#define CODEC_FLAGS_LENGTH 32
|
||||
|
||||
|
17
monitor.c
17
monitor.c
@ -487,10 +487,17 @@ static int monitor_call_pickup(int argc, const char *const *argv, struct command
|
||||
static int monitor_call_audio(int argc, const char *const *argv, struct command_line_option *o, void *context){
|
||||
struct monitor_context *c=context;
|
||||
struct vomp_call_state *call=vomp_find_call_by_session(strtol(argv[1],NULL,16));
|
||||
if (!call)
|
||||
|
||||
if (!call){
|
||||
monitor_tell_formatted(MONITOR_VOMP, "\nHANGUP:%s\n", argv[1]);
|
||||
else
|
||||
vomp_received_audio(call, atoi(argv[2]), c->buffer, c->data_expected);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int codec_type = atoi(argv[2]);
|
||||
int time = argc>=4?atoi(argv[3]):-1;
|
||||
int sequence = argc>=5?atoi(argv[4]):-1;
|
||||
|
||||
vomp_received_audio(call, codec_type, time, sequence, c->buffer, c->data_expected);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -520,7 +527,7 @@ static int monitor_call_dtmf(int argc, const char *const *argv, struct command_l
|
||||
of the majority of codec time units (70ms is the nominal
|
||||
DTMF tone length for most systems). */
|
||||
unsigned char code = digit <<4;
|
||||
vomp_received_audio(call, VOMP_CODEC_DTMF, &code, 1);
|
||||
vomp_received_audio(call, VOMP_CODEC_DTMF, -1, -1, &code, 1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -534,7 +541,7 @@ struct command_line_option monitor_options[]={
|
||||
{monitor_call, {"call","<sid>","<local_did>","<remote_did>",NULL},0,""},
|
||||
{monitor_call_ring, {"ringing","<token>",NULL},0,""},
|
||||
{monitor_call_pickup, {"pickup","<token>",NULL},0,""},
|
||||
{monitor_call_audio,{"audio","<token>","<type>","[<offset>]",NULL},0,""},
|
||||
{monitor_call_audio,{"audio","<token>","<type>","[<time>]","[<sequence>]",NULL},0,""},
|
||||
{monitor_call_hangup, {"hangup","<token>",NULL},0,""},
|
||||
{monitor_call_dtmf, {"dtmf","<token>","<digits>",NULL},0,""},
|
||||
{NULL},
|
||||
|
6
serval.h
6
serval.h
@ -617,15 +617,13 @@ int is_codec_set(int codec, unsigned char *flags);
|
||||
|
||||
struct vomp_call_state *vomp_find_call_by_session(int session_token);
|
||||
int vomp_mdp_received(overlay_mdp_frame *mdp);
|
||||
int vomp_tick_interval();
|
||||
int vomp_sample_size(int c);
|
||||
int vomp_codec_timespan(int c);
|
||||
int vomp_parse_dtmf_digit(char c);
|
||||
int vomp_dial(unsigned char *local_sid, unsigned char *remote_sid, const char *local_did, const char *remote_did);
|
||||
int vomp_pickup(struct vomp_call_state *call);
|
||||
int vomp_hangup(struct vomp_call_state *call);
|
||||
int vomp_ringing(struct vomp_call_state *call);
|
||||
int vomp_received_audio(struct vomp_call_state *call, int audio_codec, const unsigned char *audio, int audio_length);
|
||||
int vomp_received_audio(struct vomp_call_state *call, int audio_codec, int time, int sequence,
|
||||
const unsigned char *audio, int audio_length);
|
||||
void monitor_get_all_supported_codecs(unsigned char *codecs);
|
||||
|
||||
int cli_putchar(char c);
|
||||
|
@ -1,7 +1,6 @@
|
||||
SERVAL_SOURCES = $(SERVAL_BASE)audiodevices.c \
|
||||
$(SERVAL_BASE)audio_reflector.c \
|
||||
$(SERVAL_BASE)cli.c \
|
||||
$(SERVAL_BASE)codecs.c \
|
||||
$(SERVAL_BASE)commandline.c \
|
||||
$(SERVAL_BASE)conf.c \
|
||||
$(SERVAL_BASE)crypto.c \
|
||||
|
428
vomp.c
428
vomp.c
@ -124,16 +124,21 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#define VOMP_STATE_INCALL 5
|
||||
#define VOMP_STATE_CALLENDED 6
|
||||
|
||||
#define VOMP_REJECT_HANGUP 0
|
||||
#define VOMP_REJECT_NOPHONE 1
|
||||
#define VOMP_REJECT_NOCODEC 2
|
||||
#define VOMP_REJECT_BUSY 3
|
||||
#define VOMP_REJECT_TIMEOUT 4
|
||||
|
||||
#define VOMP_SESSION_MASK 0xffffff
|
||||
#define VOMP_SESSION_MASK 0xffff
|
||||
#define VOMP_MAX_CALLS 16
|
||||
|
||||
#define VOMP_VERSION 0x02
|
||||
|
||||
struct vomp_call_half {
|
||||
unsigned char sid[SID_SIZE];
|
||||
char did[64];
|
||||
unsigned char state;
|
||||
unsigned char codec;
|
||||
unsigned int session;
|
||||
unsigned int sequence;
|
||||
};
|
||||
@ -154,6 +159,8 @@ struct jitter_measurements{
|
||||
int sample_count;
|
||||
};
|
||||
|
||||
#define SEEN_SAMPLES 16
|
||||
|
||||
struct vomp_call_state {
|
||||
struct sched_ent alarm;
|
||||
struct vomp_call_half local;
|
||||
@ -162,18 +169,13 @@ struct vomp_call_state {
|
||||
time_ms_t create_time;
|
||||
time_ms_t last_activity;
|
||||
time_ms_t audio_clock;
|
||||
int remote_audio_clock;
|
||||
|
||||
// last local & remote status we sent to all interested parties
|
||||
int last_sent_status;
|
||||
int rejection_reason;
|
||||
unsigned char remote_codec_flags[CODEC_FLAGS_LENGTH];
|
||||
struct jitter_measurements jitter;
|
||||
|
||||
// track when we first heard audio, so we can calculate timing from the current sequence number
|
||||
int first_remote_audio_sequence;
|
||||
|
||||
// simple ring buffer of audio sample times, used to drop duplicate incoming frames
|
||||
// stores end times, since this is an odd number we can initialise the buffer to zero's
|
||||
int sample_pos;
|
||||
unsigned int seen_samples[VOMP_MAX_RECENT_SAMPLES *4];
|
||||
};
|
||||
|
||||
/* Some clients may only support one call at a time, even then we allow for multiple call states.
|
||||
@ -181,17 +183,65 @@ struct vomp_call_state {
|
||||
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;
|
||||
// TODO allocate call structures dynamically
|
||||
struct vomp_call_state vomp_call_states[VOMP_MAX_CALLS];
|
||||
struct profile_total vomp_stats;
|
||||
|
||||
static void vomp_process_tick(struct sched_ent *alarm);
|
||||
static const char *vomp_describe_codec(int c);
|
||||
strbuf strbuf_append_vomp_supported_codecs(strbuf sb, const unsigned char supported_codecs[256]);
|
||||
|
||||
|
||||
void store_jitter_sample(struct jitter_measurements *measurements, int sample_clock, int local_clock){
|
||||
static int vomp_codec_timespan(int c, int data_size)
|
||||
{
|
||||
switch(c) {
|
||||
case VOMP_CODEC_16SIGNED: return data_size/16;
|
||||
case VOMP_CODEC_ULAW: return data_size/8;
|
||||
case VOMP_CODEC_ALAW: return data_size/8;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 '?';
|
||||
}
|
||||
|
||||
static int store_jitter_sample(struct jitter_measurements *measurements, int sample_clock, int local_clock){
|
||||
IN();
|
||||
int i;
|
||||
int i, count=0;
|
||||
|
||||
// have a quick look through recent samples, drop if already seen
|
||||
if (measurements->sample_count>0){
|
||||
i=measurements->next_sample -1;
|
||||
while(count<SEEN_SAMPLES && count<=measurements->sample_count){
|
||||
if (i<0)
|
||||
i=measurements->sample_count -1;
|
||||
if (measurements->samples[i].sample_clock == sample_clock)
|
||||
RETURN(-1);
|
||||
i--;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
struct jitter_sample *sample = &measurements->samples[measurements->next_sample];
|
||||
|
||||
measurements->next_sample++;
|
||||
@ -246,10 +296,10 @@ void store_jitter_sample(struct jitter_measurements *measurements, int sample_cl
|
||||
if (sample_clock > measurements->max_sample_clock)
|
||||
measurements->max_sample_clock=sample_clock;
|
||||
|
||||
OUT();
|
||||
RETURN(0);
|
||||
}
|
||||
|
||||
int get_jitter_size(struct jitter_measurements *measurements){
|
||||
static int get_jitter_size(struct jitter_measurements *measurements){
|
||||
IN();
|
||||
int i=JITTER_SAMPLES -4;
|
||||
int jitter;
|
||||
@ -281,7 +331,7 @@ struct vomp_call_state *vomp_find_call_by_session(int session_token)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int vomp_generate_session_id()
|
||||
static int vomp_generate_session_id()
|
||||
{
|
||||
int session_id=0;
|
||||
while (!session_id)
|
||||
@ -302,7 +352,7 @@ int vomp_generate_session_id()
|
||||
return session_id;
|
||||
}
|
||||
|
||||
struct vomp_call_state *vomp_create_call(unsigned char *remote_sid,
|
||||
static struct vomp_call_state *vomp_create_call(unsigned char *remote_sid,
|
||||
unsigned char *local_sid,
|
||||
unsigned int remote_session,
|
||||
unsigned int local_session)
|
||||
@ -335,7 +385,7 @@ struct vomp_call_state *vomp_create_call(unsigned char *remote_sid,
|
||||
return call;
|
||||
}
|
||||
|
||||
struct vomp_call_state *vomp_find_or_create_call(unsigned char *remote_sid,
|
||||
static struct vomp_call_state *vomp_find_or_create_call(unsigned char *remote_sid,
|
||||
unsigned char *local_sid,
|
||||
unsigned int sender_session,
|
||||
unsigned int recvr_session,
|
||||
@ -390,14 +440,17 @@ struct vomp_call_state *vomp_find_or_create_call(unsigned char *remote_sid,
|
||||
return call;
|
||||
}
|
||||
|
||||
/* Don't create a call record if either party has ended it */
|
||||
if (sender_state==VOMP_STATE_CALLENDED || recvr_state==VOMP_STATE_CALLENDED)
|
||||
/* Don't create a call record if either party has already ended it */
|
||||
if (sender_state==VOMP_STATE_CALLENDED || recvr_state==VOMP_STATE_CALLENDED){
|
||||
WHYF("Not creating a call record when the call has already ended");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Only create a call record if either party is in CALLPREP state */
|
||||
if (sender_state==VOMP_STATE_CALLPREP || recvr_state==VOMP_STATE_CALLPREP)
|
||||
/* Only create a call record if the remote party is trying to prepare a call */
|
||||
if (sender_state==VOMP_STATE_CALLPREP && recvr_state==VOMP_STATE_NOCALL && recvr_session==0)
|
||||
return vomp_create_call(remote_sid, local_sid, sender_session, recvr_session);
|
||||
|
||||
WHYF("Not creating a call record for state %d %d", sender_state, recvr_state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -408,29 +461,19 @@ static void prepare_vomp_header(struct vomp_call_state *call, overlay_mdp_frame
|
||||
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;
|
||||
time_ms_t call_millis = gettime_ms() - call->create_time;
|
||||
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;
|
||||
mdp->out.payload[0]=VOMP_VERSION;
|
||||
mdp->out.payload[1]=(call->local.session>>8)&0xff;
|
||||
mdp->out.payload[2]=(call->local.session>>0)&0xff;
|
||||
mdp->out.payload[3]=(call->remote.session>>8)&0xff;
|
||||
mdp->out.payload[4]=(call->remote.session>>0)&0xff;
|
||||
mdp->out.payload[5]=(call->remote.state<<4)|call->local.state;
|
||||
mdp->out.payload_length=6;
|
||||
}
|
||||
|
||||
/* send updated call status to end-point and to any interested listeners as
|
||||
appropriate */
|
||||
|
||||
int vomp_send_status_remote(struct vomp_call_state *call)
|
||||
static int vomp_send_status_remote(struct vomp_call_state *call)
|
||||
{
|
||||
overlay_mdp_frame mdp;
|
||||
unsigned short *len=&mdp.out.payload_length;
|
||||
@ -449,8 +492,6 @@ int vomp_send_status_remote(struct vomp_call_state *call)
|
||||
for (i = 0; i < 256; ++i)
|
||||
if (is_codec_set(i,codecs)) {
|
||||
mdp.out.payload[(*len)++]=i;
|
||||
if (debug & DEBUG_VOMP)
|
||||
DEBUGF("I support the %s codec", vomp_describe_codec(i));
|
||||
}
|
||||
mdp.out.payload[(*len)++]=0;
|
||||
|
||||
@ -475,55 +516,46 @@ int vomp_send_status_remote(struct vomp_call_state *call)
|
||||
}
|
||||
|
||||
// copy audio into the rotor buffers
|
||||
int vomp_received_audio(struct vomp_call_state *call, int audio_codec, const unsigned char *audio, int audio_length)
|
||||
int vomp_received_audio(struct vomp_call_state *call, int audio_codec, int time, int sequence,
|
||||
const unsigned char *audio, int audio_length)
|
||||
{
|
||||
if (call->local.state!=VOMP_STATE_INCALL)
|
||||
return -1;
|
||||
|
||||
int codec_block_size=vomp_sample_size(audio_codec);
|
||||
int offset=0;
|
||||
int codec_duration = vomp_codec_timespan(audio_codec);
|
||||
|
||||
while(offset<audio_length){
|
||||
overlay_mdp_frame mdp;
|
||||
unsigned short *len=&mdp.out.payload_length;
|
||||
|
||||
bzero(&mdp,sizeof(mdp));
|
||||
prepare_vomp_header(call, &mdp);
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
mdp.out.payload[(*len)++]=(call->audio_clock>>24)&0xff;
|
||||
mdp.out.payload[(*len)++]=(call->audio_clock>>16)&0xff;
|
||||
mdp.out.payload[(*len)++]=(call->audio_clock>>8)&0xff;
|
||||
mdp.out.payload[(*len)++]=(call->audio_clock>>0)&0xff;
|
||||
mdp.out.payload[(*len)++]=audio_codec;
|
||||
|
||||
if (offset+codec_block_size>audio_length)
|
||||
codec_block_size = audio_length - offset;
|
||||
|
||||
bcopy(audio+offset,&mdp.out.payload[(*len)],codec_block_size);
|
||||
(*len)+=codec_block_size;
|
||||
offset+=codec_block_size;
|
||||
|
||||
call->audio_clock += codec_duration;
|
||||
call->local.sequence++;
|
||||
|
||||
// send the payload more than once to add resilience to dropped packets
|
||||
// TODO remove once network links have built in retries
|
||||
mdp.out.send_copies=VOMP_MAX_RECENT_SAMPLES;
|
||||
overlay_mdp_dispatch(&mdp,0,NULL,0);
|
||||
|
||||
// note we assume the caller will be consistent about providing time and sequence info
|
||||
if (time==-1){
|
||||
time = call->audio_clock;
|
||||
call->audio_clock+=vomp_codec_timespan(audio_codec, audio_length);
|
||||
}
|
||||
|
||||
if (sequence==-1)
|
||||
sequence = call->local.sequence++;
|
||||
|
||||
overlay_mdp_frame mdp;
|
||||
unsigned short *len=&mdp.out.payload_length;
|
||||
|
||||
bzero(&mdp,sizeof(mdp));
|
||||
prepare_vomp_header(call, &mdp);
|
||||
|
||||
mdp.out.payload[(*len)++]=audio_codec;
|
||||
time = time / 20;
|
||||
mdp.out.payload[(*len)++]=(time>>8)&0xff;
|
||||
mdp.out.payload[(*len)++]=(time>>0)&0xff;
|
||||
mdp.out.payload[(*len)++]=(sequence>>8)&0xff;
|
||||
mdp.out.payload[(*len)++]=(sequence>>0)&0xff;
|
||||
|
||||
bcopy(audio,&mdp.out.payload[(*len)],audio_length);
|
||||
(*len)+=audio_length;
|
||||
|
||||
// send the payload more than once to add resilience to dropped packets
|
||||
// TODO remove once network links have built in retries
|
||||
mdp.out.send_copies=VOMP_MAX_RECENT_SAMPLES;
|
||||
overlay_mdp_dispatch(&mdp,0,NULL,0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int monitor_call_status(struct vomp_call_state *call)
|
||||
static 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",
|
||||
@ -538,11 +570,11 @@ int monitor_call_status(struct vomp_call_state *call)
|
||||
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, int sequence)
|
||||
static int monitor_send_audio(struct vomp_call_state *call, int audio_codec, int time, int sequence,
|
||||
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.
|
||||
@ -552,21 +584,21 @@ int monitor_send_audio(struct vomp_call_state *call, int audio_codec, unsigned i
|
||||
int jitter_delay = get_jitter_size(&call->jitter);
|
||||
|
||||
int msglen = snprintf(msg, 1024,
|
||||
"\n*%d:AUDIOPACKET:%x:%d:%d:%d:%d:%d\n",
|
||||
sample_bytes,
|
||||
"\n*%d:AUDIO:%x:%d:%d:%d:%d\n",
|
||||
audio_length,
|
||||
call->local.session,
|
||||
audio_codec, start_time, end_time,
|
||||
sequence, jitter_delay);
|
||||
audio_codec, time, sequence,
|
||||
jitter_delay);
|
||||
|
||||
bcopy(audio, &msg[msglen], sample_bytes);
|
||||
msglen+=sample_bytes;
|
||||
bcopy(audio, &msg[msglen], audio_length);
|
||||
msglen+=audio_length;
|
||||
msg[msglen++]='\n';
|
||||
monitor_tell_clients(msg, msglen, MONITOR_VOMP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// update local state and notify interested clients with the correct message
|
||||
int vomp_update_local_state(struct vomp_call_state *call, int new_state){
|
||||
static int vomp_update_local_state(struct vomp_call_state *call, int new_state){
|
||||
if (call->local.state>=new_state)
|
||||
return 0;
|
||||
|
||||
@ -606,7 +638,7 @@ int vomp_update_local_state(struct vomp_call_state *call, int new_state){
|
||||
}
|
||||
|
||||
// update remote state and notify interested clients with the correct message
|
||||
int vomp_update_remote_state(struct vomp_call_state *call, int new_state){
|
||||
static int vomp_update_remote_state(struct vomp_call_state *call, int new_state){
|
||||
if (call->remote.state>=new_state)
|
||||
return 0;
|
||||
|
||||
@ -632,7 +664,7 @@ int vomp_update_remote_state(struct vomp_call_state *call, int new_state){
|
||||
}
|
||||
|
||||
// send call state updates if required.
|
||||
int vomp_update(struct vomp_call_state *call)
|
||||
static int vomp_update(struct vomp_call_state *call)
|
||||
{
|
||||
int combined_status=(call->remote.state<<4)|call->local.state;
|
||||
|
||||
@ -654,58 +686,48 @@ int vomp_update(struct vomp_call_state *call)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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
|
||||
int vomp_audio_already_seen(struct vomp_call_state *call, unsigned int end_time)
|
||||
{
|
||||
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;
|
||||
static int to_absolute_value(int short_value, int reference_value){
|
||||
short_value = (reference_value & 0xFFFF0000) | short_value;
|
||||
|
||||
if (short_value + 0x8000 < reference_value)
|
||||
short_value+=0x10000;
|
||||
|
||||
if (short_value > reference_value + 0x8000)
|
||||
short_value-=0x10000;
|
||||
|
||||
return short_value;
|
||||
}
|
||||
|
||||
int vomp_process_audio(struct vomp_call_state *call,unsigned int sender_duration,overlay_mdp_frame *mdp,time_ms_t now)
|
||||
static int vomp_process_audio(struct vomp_call_state *call, overlay_mdp_frame *mdp, time_ms_t now)
|
||||
{
|
||||
int ofs=14;
|
||||
// if (mdp->in.payload_length>14)
|
||||
// DEBUGF("got here (payload has %d bytes)",mdp->in.payload_length);
|
||||
int ofs=6;
|
||||
|
||||
/* Get end time marker for sample block collection */
|
||||
unsigned int e=0, s=0;
|
||||
if(ofs>=mdp->in.payload_length)
|
||||
return 0;
|
||||
|
||||
int sequence = call->remote.sequence;
|
||||
int codec=mdp->in.payload[ofs++];
|
||||
|
||||
if(ofs<mdp->in.payload_length)
|
||||
{
|
||||
s=mdp->in.payload[ofs++]<<24;
|
||||
s|=mdp->in.payload[ofs++]<<16;
|
||||
s|=mdp->in.payload[ofs++]<<8;
|
||||
s|=mdp->in.payload[ofs++]<<0;
|
||||
|
||||
sender_duration = (s&0xFFFF0000)|sender_duration;
|
||||
|
||||
int codec=mdp->in.payload[ofs++];
|
||||
int audio_len = mdp->in.payload_length - ofs;
|
||||
if ((!codec)||vomp_sample_size(codec)<0) return -1;
|
||||
int time = mdp->in.payload[ofs]<<8 | mdp->in.payload[ofs+1]<<0;
|
||||
ofs+=2;
|
||||
int sequence = mdp->in.payload[ofs]<<8 | mdp->in.payload[ofs+1]<<0;
|
||||
ofs+=2;
|
||||
|
||||
// rebuild absolute time value from short relative time.
|
||||
call->remote_audio_clock=to_absolute_value(time, call->remote_audio_clock);
|
||||
call->remote.sequence=to_absolute_value(sequence, call->remote.sequence);
|
||||
|
||||
time=call->remote_audio_clock * 20;
|
||||
|
||||
int audio_len = mdp->in.payload_length - ofs;
|
||||
|
||||
e = s + vomp_codec_timespan(codec) - 1;
|
||||
|
||||
/* Pass audio frame to all registered listeners */
|
||||
if (!vomp_audio_already_seen(call, e)){
|
||||
store_jitter_sample(&call->jitter, s, now);
|
||||
|
||||
if (monitor_socket_count)
|
||||
monitor_send_audio(call, codec, s, e,
|
||||
&mdp->in.payload[ofs],
|
||||
audio_len,
|
||||
sequence);
|
||||
}
|
||||
}
|
||||
if (store_jitter_sample(&call->jitter, time, now))
|
||||
return 0;
|
||||
|
||||
/* Pass audio frame to all registered listeners */
|
||||
if (monitor_socket_count)
|
||||
monitor_send_audio(call, codec, time, call->remote.sequence,
|
||||
&mdp->in.payload[ofs],
|
||||
audio_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -722,7 +744,7 @@ int vomp_ringing(struct vomp_call_state *call){
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vomp_call_destroy(struct vomp_call_state *call)
|
||||
static int vomp_call_destroy(struct vomp_call_state *call)
|
||||
{
|
||||
if (debug & DEBUG_VOMP)
|
||||
DEBUGF("Destroying call %06x:%06x [%s,%s]", call->local.session, call->remote.session, call->local.did,call->remote.did);
|
||||
@ -807,9 +829,9 @@ int vomp_hangup(struct vomp_call_state *call)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vomp_extract_remote_codec_list(struct vomp_call_state *call,overlay_mdp_frame *mdp)
|
||||
static int vomp_extract_remote_codec_list(struct vomp_call_state *call,overlay_mdp_frame *mdp)
|
||||
{
|
||||
int ofs=14;
|
||||
int ofs=6;
|
||||
|
||||
if (debug & DEBUG_VOMP)
|
||||
dump("codec list mdp frame", (unsigned char *)&mdp->in.payload[0],mdp->in.payload_length);
|
||||
@ -846,18 +868,12 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
|
||||
struct vomp_call_state *call=NULL;
|
||||
|
||||
switch(mdp->in.payload[0]) {
|
||||
case 0x01: /* Ordinary VoMP state+optional audio frame */
|
||||
case VOMP_VERSION:
|
||||
{
|
||||
int recvr_state=mdp->in.payload[1]>>4;
|
||||
int sender_state=mdp->in.payload[1]&0xf;
|
||||
unsigned int recvr_session=
|
||||
(mdp->in.payload[8]<<16)|(mdp->in.payload[9]<<8)|mdp->in.payload[10];
|
||||
unsigned int sender_session=
|
||||
(mdp->in.payload[11]<<16)|(mdp->in.payload[12]<<8)|mdp->in.payload[13];
|
||||
int sender_seq=(mdp->in.payload[4]<<8)+mdp->in.payload[5];
|
||||
|
||||
// cyclic ~1 minute timer...
|
||||
unsigned int sender_duration = (mdp->in.payload[6]<<8) | mdp->in.payload[7];
|
||||
unsigned int sender_session=(mdp->in.payload[1]<<8)|mdp->in.payload[2];
|
||||
unsigned int recvr_session=(mdp->in.payload[3]<<8)|mdp->in.payload[4];
|
||||
int recvr_state=mdp->in.payload[5]>>4;
|
||||
int sender_state=mdp->in.payload[5]&0xf;
|
||||
|
||||
/* wants to create a call session.
|
||||
Main aim here: replay protection. An adversary should not be able to
|
||||
@ -877,12 +893,13 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
|
||||
if (!recvr_session && (debug & DEBUG_VOMP))
|
||||
DEBUG("recvr_session==0, created call");
|
||||
|
||||
recvr_state = call->local.state;
|
||||
call->remote.sequence=sender_seq;
|
||||
|
||||
// stale packet or forgery attempt? Should we just drop it?
|
||||
if (sender_state < call->remote.state)
|
||||
sender_state = call->remote.state;
|
||||
|
||||
// TODO ignore state changes if sequence is stale?
|
||||
// TODO ignore state changes that seem to go backwards?
|
||||
// we don't really care what state they think we are in.
|
||||
// Though we could use this information to indicate a network error.
|
||||
recvr_state = call->local.state;
|
||||
|
||||
if ((!monitor_socket_count)
|
||||
&&(!monitor_client_interested(MONITOR_VOMP)))
|
||||
@ -890,11 +907,15 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
|
||||
/* No registered listener, so we cannot answer the call, so just reject
|
||||
it. */
|
||||
WHY("Rejecting call, no listening clients");
|
||||
call->rejection_reason=VOMP_REJECT_NOPHONE;
|
||||
recvr_state=VOMP_STATE_CALLENDED;
|
||||
/* now let the state machine progress to destroy the call */
|
||||
}
|
||||
|
||||
if (recvr_state < VOMP_STATE_RINGINGOUT && sender_state < VOMP_STATE_RINGINGOUT){
|
||||
|
||||
// TODO, pass codec list to connected clients, let them pick a codec they can use first?
|
||||
|
||||
unsigned char supported_codecs[CODEC_FLAGS_LENGTH];
|
||||
int i, found=0;
|
||||
|
||||
@ -915,6 +936,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
|
||||
// nope, we can't speak the same language.
|
||||
if (!found){
|
||||
WHY("Rejecting call, no matching codecs found");
|
||||
call->rejection_reason=VOMP_REJECT_NOCODEC;
|
||||
recvr_state=VOMP_STATE_CALLENDED;
|
||||
}
|
||||
}
|
||||
@ -922,7 +944,6 @@ int vomp_mdp_received(overlay_mdp_frame *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 */
|
||||
|
||||
recvr_state=VOMP_STATE_CALLENDED;
|
||||
}
|
||||
|
||||
@ -947,10 +968,11 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
|
||||
that they would like us to start ringing.
|
||||
So change our state to RINGINGIN. */
|
||||
|
||||
if (call->initiated_call)
|
||||
if (call->initiated_call){
|
||||
// hey, quit it, we were trying to call you.
|
||||
call->rejection_reason=VOMP_REJECT_BUSY;
|
||||
recvr_state=VOMP_STATE_CALLENDED;
|
||||
else{
|
||||
}else{
|
||||
// Don't automatically transition to RINGIN, wait for a client to tell us when.
|
||||
}
|
||||
break;
|
||||
@ -964,7 +986,6 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
|
||||
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
|
||||
recvr_state=VOMP_STATE_RINGINGOUT;
|
||||
}else{
|
||||
recvr_state=VOMP_STATE_CALLENDED;
|
||||
@ -1002,7 +1023,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
|
||||
// Fall through
|
||||
case (VOMP_STATE_INCALL<<3)|VOMP_STATE_INCALL:
|
||||
/* play any audio that they have sent us. */
|
||||
vomp_process_audio(call,sender_duration,mdp,now);
|
||||
vomp_process_audio(call,mdp,now);
|
||||
break;
|
||||
|
||||
case (VOMP_STATE_CALLENDED<<3)|VOMP_STATE_NOCALL:
|
||||
@ -1044,88 +1065,6 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
|
||||
return WHY("Malformed VoMP MDP packet?");
|
||||
}
|
||||
|
||||
static const char *vomp_describe_codec(int c)
|
||||
{
|
||||
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";
|
||||
case VOMP_CODEC_PCM: return "PCM@8KHz";
|
||||
case VOMP_CODEC_DTMF: return "DTMF";
|
||||
case VOMP_CODEC_ENGAGED: return "Engaged-tone";
|
||||
case VOMP_CODEC_ONHOLD: return "On-Hold";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
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;
|
||||
case VOMP_CODEC_PCM: return 320;
|
||||
case VOMP_CODEC_DTMF: return 1;
|
||||
case VOMP_CODEC_ENGAGED: return 0;
|
||||
case VOMP_CODEC_ONHOLD: return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
case VOMP_CODEC_PCM: return 20;
|
||||
case VOMP_CODEC_DTMF: return 80;
|
||||
case VOMP_CODEC_ENGAGED: return 20;
|
||||
case VOMP_CODEC_ONHOLD: return 20;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 '?';
|
||||
}
|
||||
|
||||
static void vomp_process_tick(struct sched_ent *alarm)
|
||||
{
|
||||
char msg[32];
|
||||
@ -1145,6 +1084,7 @@ static void vomp_process_tick(struct sched_ent *alarm)
|
||||
(call->last_activity+VOMP_CALL_NETWORK_TIMEOUT<now) ){
|
||||
|
||||
/* tell any local clients that call has died */
|
||||
call->rejection_reason=VOMP_REJECT_TIMEOUT;
|
||||
vomp_update_local_state(call, VOMP_STATE_CALLENDED);
|
||||
vomp_update_remote_state(call, VOMP_STATE_CALLENDED);
|
||||
vomp_update(call);
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include "monitor-client.h"
|
||||
#include "str.h"
|
||||
#include "constants.h"
|
||||
#include "strbuf.h"
|
||||
#include "strbuf_helpers.h"
|
||||
|
||||
int call_token=-1;
|
||||
int seen_audio=0;
|
||||
@ -301,8 +303,8 @@ int app_vomp_console(int argc, const char *const *argv, struct command_line_opti
|
||||
|
||||
monitor_client_fd = monitor_client_open(&monitor_state);
|
||||
|
||||
monitor_client_writeline(monitor_client_fd, "monitor vomp %d %d %d\n",
|
||||
VOMP_CODEC_8ULAW,VOMP_CODEC_8ALAW,VOMP_CODEC_PCM);
|
||||
monitor_client_writeline(monitor_client_fd, "monitor vomp %d\n",
|
||||
VOMP_CODEC_TEXT);
|
||||
|
||||
set_nonblock(monitor_client_fd);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user