//struct lcr_gsm *gsm = NULL;
+#define RTP_PT_GSM_HALF 96
+#define RTP_PT_GSM_EFR 97
+#define RTP_PT_GSM_AMR 98
+
int new_callref = 1;
/* names of MNCC-SAP */
const char *name;
} mncc_names[] = {
{ 0, "New call ref" },
+ { 1, "Codec negotiation" },
{ MNCC_SETUP_REQ, "MNCC_SETUP_REQ" },
{ MNCC_SETUP_IND, "MNCC_SETUP_IND" },
{ MNCC_SETUP_RSP, "MNCC_SETUP_RSP" },
{ MNCC_STOP_DTMF_REQ, "MNCC_STOP_DTMF_REQ" },
{ MNCC_HOLD_REQ, "MNCC_HOLD_REQ " },
{ MNCC_RETRIEVE_REQ, "MNCC_RETRIEVE_REQ" },
+ { MNCC_LCHAN_MODIFY, "MNCC_LCHAN_MODIFY" },
{ 0, NULL }
};
p_g_rtp_bridge = 0;
if (interface->rtp_bridge)
p_g_rtp_bridge = 1;
+ p_g_rtp_payloads = 0;
memset(&p_g_samples, 0, sizeof(p_g_samples));
SCPY(p_g_interface_name, interface->name);
p_callerinfo.itype = (interface->extension)?INFO_ITYPE_ISDN_EXTENSION:INFO_ITYPE_ISDN;
}
p_g_rxpos = 0;
p_g_tch_connected = 0;
+ p_g_payload_type = -1;
PDEBUG(DEBUG_GSM, "Created new GSMPort(%s).\n", portname);
}
SCAT(msgtext, " ----");
/* init trace with given values */
- start_trace(0,
+ start_trace(-1,
interface,
port?numberrize_callerinfo(port->p_callerinfo.id, port->p_callerinfo.ntype, options.national, options.international):NULL,
port?port->p_dialinginfo.id:NULL,
msgtext);
}
-/* PROCEEDING INDICATION */
-void Pgsm::call_conf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
+/* modify lchan to given payload type */
+void Pgsm::modify_lchan(unsigned char payload_type)
{
struct gsm_mncc *mode;
- gsm_trace_header(p_g_interface_name, this, msg_type, DIRECTION_IN);
- if (mncc->fields & MNCC_F_CAUSE) {
- add_trace("cause", "coding", "%d", mncc->cause.coding);
- add_trace("cause", "location", "%", mncc->cause.location);
- add_trace("cause", "value", "%", mncc->cause.value);
- }
- end_trace();
+ /* already modified to that payload type */
+ if (p_g_payload_type == payload_type)
+ return;
- /* modify lchan to GSM codec V1 */
+ p_g_payload_type = payload_type;
gsm_trace_header(p_g_interface_name, this, MNCC_LCHAN_MODIFY, DIRECTION_OUT);
mode = create_mncc(MNCC_LCHAN_MODIFY, p_g_callref);
- mode->lchan_mode = 0x01; /* GSM V1 */
- mode->lchan_type = 0x02;
+ switch (payload_type) {
+ case RTP_PT_GSM_EFR:
+ add_trace("speech", "version", "EFR given");
+ mode->lchan_mode = 0x21; /* GSM V2 */
+ break;
+ case RTP_PT_GSM_AMR:
+ add_trace("speech", "version", "AMR given");
+ mode->lchan_mode = 0x41; /* GSM V3 */
+ break;
+ case RTP_PT_GSM_HALF:
+ add_trace("speech", "version", "Half Rate given");
+ mode->lchan_mode = 0x05; /* GSM V1 */
+ break;
+ default:
+ add_trace("speech", "version", "Full Rate given");
+ mode->lchan_mode = 0x01; /* GSM V1 HR */
+ }
+ mode->lchan_type = 0x02; /* FIXME: unused */
add_trace("mode", NULL, "0x%02x", mode->lchan_mode);
end_trace();
send_and_free_mncc(p_g_lcr_gsm, mode->msg_type, mode);
-
}
-/* CALL PROCEEDING INDICATION */
+/* CALL PROCEEDING INDICATION (from network) */
void Pgsm::call_proc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
{
struct lcr_msg *message;
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
memcpy(&message->param.connectinfo, &p_connectinfo, sizeof(struct connect_info));
- message->param.connectinfo.rtpinfo.payload_type = 3; /* FIXME: receive payload type from peer */
+
+ /* if we have a bridge, but not yet modified, the phone accepts out requested payload.
+ * we force the first in list */
+ if (p_g_rtp_bridge) {
+ if (p_g_payload_type < 0) {
+ /* modify to first given payload */
+ modify_lchan(p_g_rtp_payload_types[0]);
+ }
+ message->param.connectinfo.rtpinfo.payload_types[0] = p_g_payload_type;
+ message->param.connectinfo.rtpinfo.payloads = 1;
+ }
if (p_g_rtp_bridge) {
struct gsm_mncc_rtp *rtp;
rtp->ip = param->progressinfo.rtpinfo.ip;
rtp->port = param->progressinfo.rtpinfo.port;
send_and_free_mncc(p_g_lcr_gsm, rtp->msg_type, rtp);
+
+ /* modify channel to accepted payload type */
+ modify_lchan(param->progressinfo.rtpinfo.payload_types[0]);
}
}
rtp->ip = param->connectinfo.rtpinfo.ip;
rtp->port = param->connectinfo.rtpinfo.port;
send_and_free_mncc(p_g_lcr_gsm, rtp->msg_type, rtp);
- }
+ /* modify channel to accepted payload type */
+ modify_lchan(param->connectinfo.rtpinfo.payload_types[0]);
+ }
}
/* MESSAGE_DISCONNECT */
signed short p_g_rxdata[160]; /* receive audio buffer */
int p_g_rxpos; /* position in audio buffer 0..159 */
int p_g_tch_connected; /* indicates if audio is connected */
+ int p_g_payload_type; /* current payload type or -1 if not set */
int p_g_rtp_bridge; /* if we use a bridge */
unsigned int p_g_rtp_ip_remote; /* stores ip */
unsigned short p_g_rtp_port_remote; /* stores port */
+ int p_g_rtp_payloads;
+ int p_g_rtp_payload_types[8];
void frame_send(void *_frame);
void frame_receive(void *_frame);
int bridge_rx(unsigned char *data, int len);
int hunt_bchannel(void);
- void call_conf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *gsm);
+ void modify_lchan(unsigned char payload_type);
void call_proc_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
void alert_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
void setup_cnf(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
struct lcr_gsm *gsm_bs = NULL;
+#define RTP_PT_GSM_FULL 3
+#define RTP_PT_GSM_HALF 96
+#define RTP_PT_GSM_EFR 97
+#define RTP_PT_GSM_AMR 98
+
/*
* DTMF stuff
*/
PDEBUG(DEBUG_GSM, "Destroyed GSM BS process(%s).\n", p_name);
}
+/* PROCEEDING INDICATION (from MS) */
+void Pgsm_bs::call_conf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
+{
+ unsigned char payload_types[8];
+ int payloads = 0;
+
+ gsm_trace_header(p_g_interface_name, this, msg_type, DIRECTION_IN);
+ if (mncc->fields & MNCC_F_CAUSE) {
+ add_trace("cause", "coding", "%d", mncc->cause.coding);
+ add_trace("cause", "location", "%", mncc->cause.location);
+ add_trace("cause", "value", "%", mncc->cause.value);
+ }
+ end_trace();
+
+ new_state(PORT_STATE_OUT_PROCEEDING);
+
+ /* get list of offered payload types
+ * if list ist empty, the FR V1 is selected */
+ select_payload_type(mncc, payload_types, &payloads, sizeof(payload_types));
+ /* if no given payload type is supported, we assume */
+ if (!payloads) {
+ payload_types[0] = RTP_PT_GSM_FULL;
+ payloads = 1;
+ }
+
+ /* select first payload type that matches the rtp list */
+ if (p_g_rtp_bridge) {
+ int i, j;
+
+ for (i = 0; i < p_g_rtp_payloads; i++) {
+ for (j = 0; j < payloads; j++) {
+ if (p_g_rtp_payload_types[i] == payload_types[j])
+ break;
+ }
+ if (j < payloads)
+ break;
+ }
+ if (i == p_g_rtp_payloads) {
+ struct lcr_msg *message;
+
+ /* payload offered by remote RTP is not supported */
+ message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.cause = 65;
+ message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+ message_put(message);
+ /* send release */
+ mncc = create_mncc(MNCC_REL_REQ, p_g_callref);
+ gsm_trace_header(p_g_interface_name, this, MNCC_REL_REQ, DIRECTION_OUT);
+ mncc->fields |= MNCC_F_CAUSE;
+ mncc->cause.coding = 3;
+ mncc->cause.location = LOCATION_PRIVATE_LOCAL;
+ mncc->cause.value = 65;
+ add_trace("cause", "coding", "%d", mncc->cause.coding);
+ add_trace("cause", "location", "%d", mncc->cause.location);
+ add_trace("cause", "value", "%d", mncc->cause.value);
+ add_trace("reason", NULL, "None of the payload types are supported by MS");
+ end_trace();
+ send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc);
+ new_state(PORT_STATE_RELEASE);
+ trigger_work(&p_g_delete);
+ }
+ modify_lchan(p_g_rtp_payload_types[i]);
+ } else {
+ /* modify to first given payload */
+ modify_lchan(payload_types[0]);
+ }
+}
+
/* DTMF INDICATION */
void Pgsm_bs::start_dtmf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc)
{
}
/*
+ * select payload type by given list or GSM V1 FR
+ * return the payload type or 0 if not given
+ */
+
+void Pgsm_bs::select_payload_type(struct gsm_mncc *mncc, unsigned char *payload_types, int *payloads, int max_payloads)
+{
+ unsigned char payload_type;
+
+ *payloads = 0;
+
+ gsm_trace_header(p_g_interface_name, this, 1 /* codec negotioation */, DIRECTION_NONE);
+ if ((mncc->fields & MNCC_F_BEARER_CAP)) {
+ /* select preferred payload type from list */
+ int i;
+
+ add_trace("bearer", "capa", "given by MS");
+ for (i = 0; mncc->bearer_cap.speech_ver[i] >= 0; i++) {
+ /* select payload type we support */
+ switch (mncc->bearer_cap.speech_ver[i]) {
+ case 0:
+ add_trace("speech", "version", "Full Rate given");
+ payload_type = RTP_PT_GSM_FULL;
+ break;
+ case 2:
+ add_trace("speech", "version", "EFR given");
+ payload_type = RTP_PT_GSM_EFR;
+ break;
+ case 4:
+ add_trace("speech", "version", "AMR given");
+ payload_type = RTP_PT_GSM_AMR;
+ break;
+ case 1:
+ add_trace("speech", "version", "Half Rate given");
+ payload_type = RTP_PT_GSM_HALF;
+ break;
+ default:
+ add_trace("speech", "version", "%d given", mncc->bearer_cap.speech_ver[i]);
+ payload_type = 0;
+ }
+ /* wen don't support it, so we check the next */
+ if (!payload_type) {
+ add_trace("speech", "ignored", "Not supported by LCR");
+ continue;
+ }
+ if (!p_g_rtp_bridge) {
+ if (payload_type != RTP_PT_GSM_FULL) {
+ add_trace("speech", "ignored", "Not suitable for LCR");
+ continue;
+ }
+ }
+ if (*payloads <= max_payloads) {
+ payload_types[*payloads] = payload_type;
+ (*payloads)++;
+ }
+ }
+ } else {
+ add_trace("bearer", "capa", "not given by MS");
+ add_trace("speech", "version", "Full Rate given");
+ payload_types[0] = RTP_PT_GSM_FULL;
+ *payloads = 1;
+ }
+ if (!(*payloads))
+ add_trace("error", "", "All given payload types unsupported");
+ end_trace();
+}
+
+/*
* handles all indications
*/
/* SETUP INDICATION */
{
class Endpoint *epoint;
struct lcr_msg *message;
- struct gsm_mncc *mode, *proceeding, *frame;
+ struct gsm_mncc *proceeding, *frame;
struct interface *interface;
+ unsigned char payload_types[8];
+ int payloads = 0;
interface = getinterfacebyname(p_g_interface_name);
if (!interface) {
p_dialinginfo.sending_complete = 1;
/* bearer capability */
- // todo
p_capainfo.bearer_capa = INFO_BC_SPEECH;
p_capainfo.bearer_info1 = (options.law=='a')?3:2;
p_capainfo.bearer_mode = INFO_BMODE_CIRCUIT;
p_capainfo.source_mode = B_MODE_TRANSPARENT;
p_g_mode = p_capainfo.source_mode;
+ /* get list of offered payload types
+ * if list ist empty, the FR V1 is selected */
+ select_payload_type(mncc, payload_types, &payloads, sizeof(payload_types));
+ /* if no given payload type is supported, we assume */
+ if (!payloads) {
+ payload_types[0] = RTP_PT_GSM_FULL;
+ payloads = 1;
+ }
+#if 0
+ /* if no given payload type is supported, we reject the call */
+ if (!payloads) {
+ mncc = create_mncc(MNCC_REJ_REQ, callref);
+ gsm_trace_header(p_g_interface_name, this, MNCC_REJ_REQ, DIRECTION_OUT);
+ mncc->fields |= MNCC_F_CAUSE;
+ mncc->cause.coding = 3;
+ mncc->cause.location = 1;
+ mncc->cause.value = 65;
+ add_trace("cause", "coding", "%d", mncc->cause.coding);
+ add_trace("cause", "location", "%d", mncc->cause.location);
+ add_trace("cause", "value", "%d", mncc->cause.value);
+ add_trace("reason", NULL, "Given speech codec(s) not supported");
+ end_trace();
+ send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc);
+ new_state(PORT_STATE_RELEASE);
+ trigger_work(&p_g_delete);
+ return;
+ }
+#endif
+
/* useruser */
/* what infos did we got ... */
epoint->ep_app = new_endpointapp(epoint, 0, interface->app); //incoming
epointlist_new(epoint->ep_serial);
- /* modify lchan to GSM codec V1 */
- gsm_trace_header(p_g_interface_name, this, MNCC_LCHAN_MODIFY, DIRECTION_OUT);
- mode = create_mncc(MNCC_LCHAN_MODIFY, p_g_callref);
- mode->lchan_mode = 0x01; /* GSM V1 */
- mode->lchan_type = 0x02;
- add_trace("mode", NULL, "0x%02x", mode->lchan_mode);
- end_trace();
- send_and_free_mncc(p_g_lcr_gsm, mode->msg_type, mode);
+ /* modify lchan in case of no rtp bridge */
+ if (!p_g_rtp_bridge)
+ modify_lchan(payload_types[0]);
/* send call proceeding */
gsm_trace_header(p_g_interface_name, this, MNCC_CALL_PROC_REQ, DIRECTION_OUT);
SCPY((char *)message->param.setup.useruser.data, (char *)mncc->useruser.info);
message->param.setup.useruser.len = strlen(mncc->useruser.info);
message->param.setup.useruser.protocol = mncc->useruser.proto;
- message->param.setup.rtpinfo.payload_type = 3; /* FIXME: receive payload type from peer */
-
if (p_g_rtp_bridge) {
struct gsm_mncc_rtp *rtp;
+ int i;
PDEBUG(DEBUG_GSM, "Request RTP peer info, before forwarding setup\n");
p_g_setup_pending = message;
rtp = (struct gsm_mncc_rtp *) create_mncc(MNCC_RTP_CREATE, p_g_callref);
send_and_free_mncc(p_g_lcr_gsm, rtp->msg_type, rtp);
+
+ for (i = 0; i < (int)sizeof(message->param.setup.rtpinfo.payload_types) && i < payloads; i++) {
+ message->param.setup.rtpinfo.payload_types[i] = payload_types[i];
+ message->param.setup.rtpinfo.payloads++;
+ }
+
} else
message_put(message);
return;
}
+ /* unsupported codec for RTP bridge */
+ if (param->setup.rtpinfo.port) {
+ int i;
+
+ p_g_rtp_payloads = 0;
+ gsm_trace_header(p_g_interface_name, this, 1 /* codec negotioation */, DIRECTION_NONE);
+ for (i = 0; i < param->setup.rtpinfo.payloads; i++) {
+ switch (param->setup.rtpinfo.payload_types[i]) {
+ case RTP_PT_GSM_FULL:
+ case RTP_PT_GSM_EFR:
+ case RTP_PT_GSM_AMR:
+ case RTP_PT_GSM_HALF:
+ add_trace("rtp", "payload", "%d supported", param->setup.rtpinfo.payload_types[i]);
+ if (p_g_rtp_payloads < (int)sizeof(p_g_rtp_payload_types)) {
+ p_g_rtp_payload_types[p_g_rtp_payloads++] = param->setup.rtpinfo.payload_types[i];
+ p_g_rtp_payloads++;
+ }
+ break;
+ default:
+ add_trace("rtp", "payload", "%d unsupported", param->setup.rtpinfo.payload_types[i]);
+ }
+ }
+ end_trace();
+ if (!p_g_rtp_payloads) {
+ gsm_trace_header(p_g_interface_name, this, MNCC_SETUP_REQ, DIRECTION_OUT);
+ add_trace("failure", NULL, "No payload given that is supported by GSM");
+ end_trace();
+ message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_RELEASE);
+ message->param.disconnectinfo.cause = 65;
+ message->param.disconnectinfo.location = LOCATION_PRIVATE_LOCAL;
+ message_put(message);
+ new_state(PORT_STATE_RELEASE);
+ trigger_work(&p_g_delete);
+ return;
+ }
+ }
+
// SCPY(&p_m_tones_dir, param->setup.ext.tones_dir);
/* screen outgoing caller id */
do_screen(1, p_callerinfo.id, sizeof(p_callerinfo.id), &p_callerinfo.ntype, &p_callerinfo.present, p_g_interface_name);
add_trace("redir", "screen", "%d", mncc->redirecting.screen);
add_trace("redir", "number", "%s", mncc->redirecting.number);
}
- /* bearer capability */
- //todo
end_trace();
send_and_free_mncc(p_g_lcr_gsm, mncc->msg_type, mncc);
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_PROCEEDING);
message_put(message);
- new_state(PORT_STATE_OUT_PROCEEDING);
-
/* RTP bridge */
if (param->setup.rtpinfo.port) {
p_g_rtp_bridge = 1;
unsigned char *p_g_dtmf; /* DTMF tone generation (MS only) */
int p_g_dtmf_index; /* DTMF tone generation index */
+ void select_payload_type(struct gsm_mncc *mncc, unsigned char *payload_types, int *payloads, int max_payloads);
void setup_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
+ void call_conf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *gsm);
void start_dtmf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
void stop_dtmf_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
void hold_ind(unsigned int msg_type, unsigned int callref, struct gsm_mncc *mncc);
#define DTMF_ST_STOP 3 /* DTMF stopped, waiting for resp. */
#define DTMF_ST_SPACE 4 /* wait space between tones */
+#define RTP_PT_GSM_FULL 3
+
/*
* constructor
*/
{
class Endpoint *epoint;
struct lcr_msg *message;
- struct gsm_mncc *mode, *proceeding, *frame;
+ struct gsm_mncc *proceeding, *frame;
struct interface *interface;
interface = getinterfacebyname(p_g_interface_name);
epointlist_new(epoint->ep_serial);
/* modify lchan to GSM codec V1 */
- gsm_trace_header(p_g_interface_name, this, MNCC_LCHAN_MODIFY, DIRECTION_OUT);
- mode = create_mncc(MNCC_LCHAN_MODIFY, p_g_callref);
- mode->lchan_mode = 0x01; /* GSM V1 */
- mode->lchan_type = 0x02;
- add_trace("mode", NULL, "0x%02x", mode->lchan_mode);
- end_trace();
- send_and_free_mncc(p_g_lcr_gsm, mode->msg_type, mode);
+ modify_lchan(RTP_PT_GSM_FULL);
/* send call proceeding */
gsm_trace_header(p_g_interface_name, this, MNCC_CALL_CONF_REQ, DIRECTION_OUT);
}
static int inter_rtp_bridge(struct interface *interface, char *filename, int line, char *parameter, char *value)
{
+ int supported = 0;
+
+#ifdef WITH_GSM_BS
+ if (interface->gsm_bs)
+ supported = 1;
+#endif
+#ifdef WITH_SIP
+ if (interface->sip)
+ supported = 1;
+#endif
+ if (!supported) {
+ SPRINT(interface_error, "Error in %s (line %d): Interface does not support RTP\n", filename, line);
+ return(-1);
+ }
interface->rtp_bridge = 1;
return(0);
}
+#if 0
+static int inter_rtp_payload(struct interface *interface, char *filename, int line, char *parameter, char *value)
+{
+#ifndef WITH_GSM_BS
+ SPRINT(interface_error, "Error in %s (line %d): GSM BS side not compiled in.\n", filename, line);
+ return(-1);
+#else
+ if (!interface->gsm_bs) {
+ SPRINT(interface_error, "Error in %s (line %d): This parameter only works for GSM BS side interface\n", filename, line);
+ return(-1);
+ }
+ if (!interface->rtp_bridge) {
+ SPRINT(interface_error, "Error in %s (line %d): This parameter only works here, if RTP bridging is enabled\n", filename, line);
+ return(-1);
+ }
+ if (!value[0]) {
+ SPRINT(interface_error, "Error in %s (line %d): parameter '%s' expects one payload type\n", filename, line, parameter);
+ return(-1);
+ }
+ if (interface->gsm_bs_payloads == sizeof(interface->gsm_bs_payload_types)) {
+ SPRINT(interface_error, "Error in %s (line %d): Too many payload types defined\n", filename, line);
+ return(-1);
+ }
+ interface->gsm_bs_payload_types[interface->gsm_bs_payloads++] = atoi(value);
+
+ return(0);
+#endif
+}
+#endif
static int inter_nonotify(struct interface *interface, char *filename, int line, char *parameter, char *value)
{
struct interface_port *ifport;
"Sets up SIP interface that represents one SIP endpoint.\n"
"Give SIP configuration file."},
{"rtp-bridge", &inter_rtp_bridge, "",
- "Sets up SIP interface that represents one SIP endpoint.\n"
- "Give SIP configuration file."},
+ "Enables RTP bridging directly from this interface.\n"
+ "This only works, if both ends support RTP. (like gsm-bs and sip)"},
+#if 0
+ not needed, since ms defines what is supports and remote (sip) tells what is selected
+ {"rtp-payload", &inter_rtp_payload, "<codec>",
+ "Define RTP payload to use. Only valid in conjuntion with gsm-bs!\n"
+ "If multiple payloads are defined, the first has highest priority.\n"
+ "If none are defined, GSM fullrate V1 (type 3) is assumed.\n"},
+#endif
{"nonotify", &inter_nonotify, "",
"Prevents sending notify messages to this interface. A call placed on hold will\n"
"Not affect the remote end (phone or telcom switch).\n"
int bf_len; /* filter length of blowfish */
#ifdef WITH_GSM_BS
int gsm_bs; /* interface is an GSM BS interface */
+#if 0
+ int gsm_bs_payloads;
+ unsigned char gsm_bs_payload_types[8];
+#endif
#endif
#ifdef WITH_GSM_MS
int gsm_ms; /* interface is an GSM MS interface */
/* safe strcat/strncat */
-#define SCAT(dst, src) scat(dst, src, sizeof(dst)-strlen(dst)-1)
+#define SCAT(dst, src) scat(dst, src, sizeof(dst))
static inline void scat(char *dst, const char *src, unsigned int siz)
{
- strncat(dst, src, siz);
+ strncat(dst, src, siz-strlen(dst)-1);
dst[siz-1] = '\0';
}
*/
//#define BUDETECT_DEF
+/* internal limit of payload type in a message */
+
#ifdef BUDETECT_DEF
#define BUDETECT budetect(__FILE__, __LINE__, __FUNCTION__);
void budetect(const char *file, int line, const char *function);
/* rtp-info structure */
struct rtp_info {
- int payload_type;
- unsigned int ip;
- unsigned short port;
+ int payloads; /* number of payloads offered */
+ unsigned char payload_types[32];/* rtp payload types */
+ unsigned int ip; /* peer's IP */
+ unsigned short port; /* peer's port */
};
/* call-info structure CALLER */
memset(&p_s_rtcp_sin_local, 0, sizeof(p_s_rtcp_sin_local));
memset(&p_s_rtp_sin_remote, 0, sizeof(p_s_rtp_sin_remote));
memset(&p_s_rtcp_sin_remote, 0, sizeof(p_s_rtcp_sin_remote));
- p_s_rtp_payload_type = 0;
p_s_rtp_ip_local = 0;
p_s_rtp_ip_remote = 0;
p_s_rtp_port_local = 0;
#define RTP_PT_GSM_FULL 3
#define RTP_PT_GSM_HALF 96
#define RTP_PT_GSM_EFR 97
-#define RTP_PT_AMR 98
+#define RTP_PT_GSM_AMR 98
/* decode an rtp frame */
static int rtp_decode(class Psip *psip, unsigned char *data, int len)
return -EINVAL;
}
break;
+ case RTP_PT_GSM_HALF:
+ if (payload_len != 14) {
+ PDEBUG(DEBUG_SIP, "received RTP half rate frame with "
+ "payload length != 14 (len = %d)\n",
+ payload_len);
+ return -EINVAL;
+ }
+ break;
case RTP_PT_ALAW:
if (options.law != 'a') {
PDEBUG(DEBUG_SIP, "received Alaw, but we don't do Alaw\n");
payload_len = 31;
duration = 160;
break;
+ case RTP_PT_GSM_HALF:
+ payload_len = 14;
+ duration = 160;
+ break;
case RTP_PT_ALAW:
case RTP_PT_ULAW:
payload_len = len;
char sdp_str[256];
struct in_addr ia;
struct lcr_msg *message;
+ unsigned char payload_type;
if (param->connectinfo.rtpinfo.port) {
PDEBUG(DEBUG_SIP, "RTP info given by remote, forward that\n");
p_s_rtp_bridge = 1;
- p_s_rtp_payload_type = param->connectinfo.rtpinfo.payload_type;
+ payload_type = param->connectinfo.rtpinfo.payload_types[0];
p_s_rtp_ip_local = param->connectinfo.rtpinfo.ip;
p_s_rtp_port_local = param->connectinfo.rtpinfo.port;
+ PDEBUG(DEBUG_SIP, "payload type %d\n", payload_type);
PDEBUG(DEBUG_SIP, "local ip %08x port %d\n", p_s_rtp_ip_local, p_s_rtp_port_local);
PDEBUG(DEBUG_SIP, "remote ip %08x port %d\n", p_s_rtp_ip_remote, p_s_rtp_port_remote);
} else {
PDEBUG(DEBUG_SIP, "RTP info not given by remote, so we do our own RTP\n");
- p_s_rtp_payload_type = (options.law=='a') ? RTP_PT_ALAW : RTP_PT_ULAW;
+ payload_type = (options.law=='a') ? RTP_PT_ALAW : RTP_PT_ULAW;
/* open local RTP peer (if not bridging) */
if (rtp_connect() < 0) {
nua_cancel(p_s_handle, TAG_END());
"t=0 0\n"
"m=audio %d RTP/AVP %d\n"
"a=rtpmap:%d %s/8000\n"
- , inet_ntoa(ia), inet_ntoa(ia), p_s_rtp_port_local, p_s_rtp_payload_type, p_s_rtp_payload_type, payload_type2name(p_s_rtp_payload_type));
+ , inet_ntoa(ia), inet_ntoa(ia), p_s_rtp_port_local, payload_type, payload_type, payload_type2name(payload_type));
PDEBUG(DEBUG_SIP, "Using SDP response: %s\n", sdp_str);
nua_respond(p_s_handle, SIP_200_OK,
sip_trace_header(this, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "200 OK");
add_trace("reason", NULL, "call connected");
+ add_trace("rtp", "ip", "%s", inet_ntoa(ia));
+ add_trace("rtp", "port", "%d,%d", p_s_rtp_port_local, p_s_rtp_port_local + 1);
+ add_trace("rtp", "payload", "%d", payload_type);
end_trace();
return 0;
char to[128];
const char *local = inst->local_ip;
const char *remote = inst->remote_ip;
- char sdp_str[256];
+ char sdp_str[256], pt_str[32];
struct in_addr ia;
struct epoint_list *epointlist;
sip_cseq_t *cseq = NULL;
struct lcr_msg *message;
+ unsigned char lcr_payload = { (options.law=='a') ? RTP_PT_ALAW : RTP_PT_ULAW };
+ unsigned char *payload_types;
+ int payloads = 0;
+ int i;
PDEBUG(DEBUG_SIP, "Doing Setup (inst %p)\n", inst);
if (param->setup.rtpinfo.port) {
PDEBUG(DEBUG_SIP, "RTP info given by remote, forward that\n");
p_s_rtp_bridge = 1;
- p_s_rtp_payload_type = param->setup.rtpinfo.payload_type;
+ payload_types = param->setup.rtpinfo.payload_types;
+ payloads = param->setup.rtpinfo.payloads;
p_s_rtp_ip_local = param->setup.rtpinfo.ip;
p_s_rtp_port_local = param->setup.rtpinfo.port;
PDEBUG(DEBUG_SIP, "local ip %08x port %d\n", p_s_rtp_ip_local, p_s_rtp_port_local);
} else {
PDEBUG(DEBUG_SIP, "RTP info not given by remote, so we do our own RTP\n");
p_s_rtp_bridge = 0;
- p_s_rtp_payload_type = (options.law=='a') ? RTP_PT_ALAW : RTP_PT_ULAW;
+ payload_types = &lcr_payload;
+ payloads = 1;
/* open local RTP peer (if not bridging) */
if (rtp_open() < 0) {
return 0;
}
}
- SPRINT(from, "sip:%s@%s", param->setup.callerinfo.id, local);
- SPRINT(to, "sip:%s@%s", param->setup.dialinginfo.id, remote);
-
- sip_trace_header(this, "INVITE", DIRECTION_OUT);
- add_trace("from", "uri", "%s", from);
- add_trace("to", "uri", "%s", to);
- add_trace("rtp", "port", "%d,%d", p_s_rtp_port_local, p_s_rtp_port_local + 1);
- end_trace();
p_s_handle = nua_handle(inst->nua, NULL, TAG_END());
if (!p_s_handle) {
"s=SIP Call\n"
"c=IN IP4 %s\n"
"t=0 0\n"
- "m=audio %d RTP/AVP %d\n"
- "a=rtpmap:%d %s/8000\n"
- , inet_ntoa(ia), inet_ntoa(ia), p_s_rtp_port_local, p_s_rtp_payload_type, p_s_rtp_payload_type, payload_type2name(p_s_rtp_payload_type));
+ "m=audio %d RTP/AVP"
+ , inet_ntoa(ia), inet_ntoa(ia), p_s_rtp_port_local);
+ for (i = 0; i < payloads; i++) {
+ SPRINT(pt_str, " %d", payload_types[i]);
+ SCAT(sdp_str, pt_str);
+ }
+ SCAT(sdp_str, "\n");
+ for (i = 0; i < payloads; i++) {
+ SPRINT(pt_str, "a=rtpmap:%d %s/8000\n", payload_types[i], payload_type2name(payload_types[i]));
+ SCAT(sdp_str, pt_str);
+ }
PDEBUG(DEBUG_SIP, "Using SDP for invite: %s\n", sdp_str);
+ SPRINT(from, "sip:%s@%s", param->setup.callerinfo.id, local);
+ SPRINT(to, "sip:%s@%s", param->setup.dialinginfo.id, remote);
+
+ sip_trace_header(this, "INVITE", DIRECTION_OUT);
+ add_trace("from", "uri", "%s", from);
+ add_trace("to", "uri", "%s", to);
+ add_trace("rtp", "ip", "%s", inet_ntoa(ia));
+ add_trace("rtp", "port", "%d,%d", p_s_rtp_port_local, p_s_rtp_port_local + 1);
+ for (i = 0; i < payloads; i++)
+ add_trace("rtp", "payload", "%d", payload_types[i]);
+ end_trace();
+
// cseq = sip_cseq_create(sip_home, 123, SIP_METHOD_INVITE);
nua_invite(p_s_handle,
message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_OVERLAP);
message_put(message);
#else
- PDEBUG(DEBUG_SIP, "do overlap\n");
+ PDEBUG(DEBUG_SIP, "do proceeding\n");
new_state(PORT_STATE_OUT_PROCEEDING);
message = message_create(p_serial, epoint_id, PORT_TO_EPOINT, MESSAGE_PROCEEDING);
message_put(message);
return 0;
}
-int Psip::parse_sdp(sip_t const *sip, unsigned int *ip, unsigned short *port, uint8_t payload_type)
+int Psip::parse_sdp(sip_t const *sip, unsigned int *ip, unsigned short *port, uint8_t *payload_types, int *payloads, int max_payloads)
{
- int codec_supported = 0;
+ *payloads = 0;
if (!sip->sip_payload) {
PDEBUG(DEBUG_SIP, "no payload given\n");
}
for (map = m->m_rtpmaps; map; map = map->rm_next) {
PDEBUG(DEBUG_SIP, "RTPMAP: coding:'%s' rate='%d' pt='%d'\n", map->rm_encoding, map->rm_rate, map->rm_pt);
- if (map->rm_pt == payload_type) {
- PDEBUG(DEBUG_SIP, "supported codec found\n");
- codec_supported = 1;
- goto done_codec;
+ /* append to payload list, if there is space */
+ add_trace("rtp", "payload", "%d", map->rm_pt);
+ if (*payloads <= max_payloads) {
+ *payload_types++ = map->rm_pt;
+ (*payloads)++;
}
}
}
- done_codec:
sdp_parser_free(parser);
- if (!codec_supported)
- return 415;
-
return 0;
}
int ret;
class Endpoint *epoint;
struct lcr_msg *message;
- uint8_t payload_type;
struct interface *interface;
+ uint8_t payload_types[32];
+ int payloads = 0;
interface = getinterfacebyname(inst->interface_name);
if (!interface) {
to = sip->sip_to->a_url->url_user;
PDEBUG(DEBUG_SIP, "invite received (%s->%s)\n", from, to);
- if (p_s_rtp_bridge)
- payload_type = 3; // FIXME: use list and forward list to remote side
- else
- payload_type = (options.law=='a')?RTP_PT_ALAW:RTP_PT_ULAW;
- ret = parse_sdp(sip, &p_s_rtp_ip_remote, &p_s_rtp_port_remote, payload_type);
+ sip_trace_header(this, "Payload received", DIRECTION_NONE);
+ ret = parse_sdp(sip, &p_s_rtp_ip_remote, &p_s_rtp_port_remote, payload_types, &payloads, sizeof(payload_types));
+ if (!ret) {
+ /* if no RTP bridge, we must support LAW codec, otherwise we forward what we have */
+ if (!p_s_rtp_bridge) {
+ int i;
+
+ /* check if supported payload type exists */
+ for (i = 0; i < payloads; i++) {
+ if (payload_types[i] == ((options.law=='a') ? RTP_PT_ALAW : RTP_PT_ULAW))
+ break;
+ }
+ if (i == payloads) {
+ add_trace("error", NULL, "Expected LAW payload type (not bridged)");
+ ret = 415;
+ }
+ }
+ }
+ end_trace();
if (ret) {
if (ret == 400)
nua_respond(nh, SIP_400_BAD_REQUEST, TAG_END());
// message->param.setup.useruser.len = strlen(mncc->useruser.info);
// message->param.setup.useruser.protocol = mncc->useruser.proto;
if (p_s_rtp_bridge) {
+ int i;
+
PDEBUG(DEBUG_SIP, "sending setup with RTP info\n");
- message->param.setup.rtpinfo.payload_type = payload_type;
message->param.setup.rtpinfo.ip = p_s_rtp_ip_remote;
message->param.setup.rtpinfo.port = p_s_rtp_port_remote;
+ /* add codecs to setup message */
+ for (i = 0; i < payloads; i++) {
+ message->param.setup.rtpinfo.payload_types[message->param.setup.rtpinfo.payloads++] = payload_types[i];
+ if (message->param.setup.rtpinfo.payloads == sizeof(message->param.setup.rtpinfo.payload_types))
+ break;
+ }
}
message_put(message);
}
{
struct lcr_msg *message;
int cause = 0, location = 0;
+ uint8_t payload_types[32];
+ int payloads = 0;
PDEBUG(DEBUG_SIP, "response to invite received (status = %d)\n", status);
/* connect audio */
if (status == 183 || (status >= 200 && status <= 299)) {
int ret;
- uint8_t payload_type;
- if (p_s_rtp_bridge)
- payload_type = p_s_rtp_payload_type;
- else
- payload_type = (options.law=='a')?RTP_PT_ALAW:RTP_PT_ULAW;
- ret = parse_sdp(sip, &p_s_rtp_ip_remote, &p_s_rtp_port_remote, payload_type);
+ sip_trace_header(this, "Payload received", DIRECTION_NONE);
+ ret = parse_sdp(sip, &p_s_rtp_ip_remote, &p_s_rtp_port_remote, payload_types, &payloads, sizeof(payload_types));
+ if (!ret) {
+ if (payloads != 1)
+ ret = 415;
+ else if (!p_s_rtp_bridge) {
+ if (payload_types[0] != ((options.law=='a') ? RTP_PT_ALAW : RTP_PT_ULAW)) {
+ add_trace("error", NULL, "Expected LAW payload type (not bridged)");
+ ret = 415;
+ }
+ }
+ }
+ end_trace();
if (ret) {
- if (ret == 400)
- nua_cancel(nh, TAG_END());
- else
- nua_cancel(nh, TAG_END());
+ nua_cancel(nh, TAG_END());
sip_trace_header(this, "CANCEL", DIRECTION_OUT);
- add_trace("reason", NULL, "offered codec does not match");
+ add_trace("reason", NULL, "accepted codec does not match");
end_trace();
cause = 88;
location = LOCATION_PRIVATE_LOCAL;
message->param.progressinfo.progress = 8;
message->param.progressinfo.location = 10;
if (p_s_rtp_bridge) {
- message->param.progressinfo.rtpinfo.payload_type = p_s_rtp_payload_type;
message->param.progressinfo.rtpinfo.ip = p_s_rtp_ip_remote;
message->param.progressinfo.rtpinfo.port = p_s_rtp_port_remote;
+ message->param.progressinfo.rtpinfo.payload_types[0] = payload_types[0];
+ message->param.progressinfo.rtpinfo.payloads = 1;
}
message_put(message);
return;
new_state(PORT_STATE_CONNECT);
message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CONNECT);
if (p_s_rtp_bridge) {
- message->param.connectinfo.rtpinfo.payload_type = p_s_rtp_payload_type;
message->param.connectinfo.rtpinfo.ip = p_s_rtp_ip_remote;
message->param.connectinfo.rtpinfo.port = p_s_rtp_port_remote;
+ message->param.connectinfo.rtpinfo.payload_types[0] = payload_types[0];
+ message->param.connectinfo.rtpinfo.payloads = 1;
}
message_put(message);
return;
nua_handle_t *p_s_handle;
nua_magic_t *p_s_magic;
int p_s_rtp_bridge; /* bridge RTP instead of having a local RTP peer */
- uint8_t p_s_rtp_payload_type;
unsigned short p_s_rtp_port_local;
unsigned short p_s_rtp_port_remote;
unsigned int p_s_rtp_ip_local;
unsigned char p_s_rxdata[160]; /* receive audio buffer */
int p_s_rxpos; /* position in audio buffer 0..159 */
int bridge_rx(unsigned char *data, int len);
- int parse_sdp(sip_t const *sip, unsigned int *ip, unsigned short *port, uint8_t payload_type);
+ int parse_sdp(sip_t const *sip, unsigned int *ip, unsigned short *port, uint8_t *payload_types, int *payloads, int max_payloads);
void rtp_shutdown(void);
};