unsigned char flip[256];
+int any_sip_interface = 0;
+
//pthread_mutex_t mutex_msg;
su_home_t sip_home[1];
};
static int delete_event(struct lcr_work *work, void *instance, int index);
+static int load_timer(struct lcr_timer *timer, void *instance, int index);
/*
* initialize SIP port
*/
-Psip::Psip(int type, char *portname, struct port_settings *settings, struct interface *interface) : Port(type, portname, settings)
+Psip::Psip(int type, char *portname, struct port_settings *settings, struct interface *interface) : Port(type, portname, settings, interface)
{
p_s_rtp_bridge = 0;
if (interface->rtp_bridge)
p_s_rxpos = 0;
p_s_rtp_tx_action = 0;
+ /* audio */
+ memset(&p_s_loadtimer, 0, sizeof(p_s_loadtimer));
+ add_timer(&p_s_loadtimer, load_timer, this, 0);
+ p_s_next_tv_sec = 0;
+
PDEBUG(DEBUG_SIP, "Created new Psip(%s).\n", portname);
if (!p_s_sip_inst)
FATAL("No SIP instance for interface\n");
{
PDEBUG(DEBUG_SIP, "Destroyed SIP process(%s).\n", p_name);
+ del_timer(&p_s_loadtimer);
del_work(&p_s_delete);
rtp_close();
}
-const char *media_type2name(uint8_t media_type) {
+static const char *media_type2name(uint8_t media_type) {
switch (media_type) {
case MEDIA_TYPE_ULAW:
return "PCMU";
return 0;
}
+ /* record audio */
+ if (psip->p_record)
+ psip->record(payload, payload_len, 0); // from down
+ if (psip->p_tap)
+ psip->tap(payload, payload_len, 0); // from down
+
n = payload_len;
from = payload;
to = payload;
}
#define RTP_PORT_BASE 30000
-static unsigned int next_udp_port = RTP_PORT_BASE;
+#define RTP_PORT_MAX 39998
+static unsigned short next_udp_port = RTP_PORT_BASE;
static int rtp_sub_socket_bind(int fd, struct sockaddr_in *sin_local, uint32_t ip, uint16_t port)
{
int Psip::rtp_open(void)
{
- int rc;
+ int rc, rc2;
struct in_addr ia;
unsigned int ip;
+ unsigned short start_port;
/* create socket */
rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (!rc) {
+ if (rc < 0) {
rtp_close();
return -EIO;
}
register_fd(&p_s_rtp_fd, LCR_FD_READ, rtp_sock_callback, this, 0);
rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (!rc) {
+ if (rc < 0) {
rtp_close();
return -EIO;
}
/* bind socket */
ip = htonl(INADDR_ANY);
ia.s_addr = ip;
- for (next_udp_port = next_udp_port % 0xffff;
- next_udp_port < 0xffff; next_udp_port += 2) {
+ start_port = next_udp_port;
+ while (1) {
rc = rtp_sub_socket_bind(p_s_rtp_fd.fd, &p_s_rtp_sin_local, ip, next_udp_port);
if (rc != 0)
- continue;
+ goto try_next_port;
+
+ rc = rtp_sub_socket_bind(p_s_rtcp_fd.fd, &p_s_rtcp_sin_local, ip, next_udp_port + 1);
+ if (rc == 0) {
+ p_s_rtp_port_local = next_udp_port;
+ next_udp_port = (next_udp_port + 2 > RTP_PORT_MAX) ? RTP_PORT_BASE : next_udp_port + 2;
+ break;
+ }
+ /* reopen rtp socket and try again with next udp port */
+ unregister_fd(&p_s_rtp_fd);
+ close(p_s_rtp_fd.fd);
+ p_s_rtp_fd.fd = 0;
+ rc2 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (rc2 < 0) {
+ rtp_close();
+ return -EIO;
+ }
+ p_s_rtp_fd.fd = rc2;
+ register_fd(&p_s_rtp_fd, LCR_FD_READ, rtp_sock_callback, this, 0);
- rc = rtp_sub_socket_bind(p_s_rtcp_fd.fd, &p_s_rtcp_sin_local, ip, next_udp_port+1);
- if (rc == 0)
+try_next_port:
+ next_udp_port = (next_udp_port + 2 > RTP_PORT_MAX) ? RTP_PORT_BASE : next_udp_port + 2;
+ if (next_udp_port == start_port)
break;
+ /* we must use rc2, in order to preserve rc */
}
if (rc < 0) {
PDEBUG(DEBUG_SIP, "failed to find port\n");
rtp_close();
return rc;
}
- p_s_rtp_port_local = next_udp_port;
p_s_rtp_ip_local = ntohl(p_s_rtp_sin_local.sin_addr.s_addr);
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);
int duration; /* in samples */
unsigned char buffer[256];
+ /* record audio */
+ if (p_record)
+ record(data, len, 1); // from up
+ if (p_tap)
+ tap(data, len, 1); // from up
+
if (!p_s_rtp_is_connected) {
/* drop silently */
return 0;
/* receive from remote */
int Psip::bridge_rx(unsigned char *data, int len)
{
+ /* don't bridge, if tones are provided */
+ if (p_tone_name[0])
+ return -EBUSY;
+
/* write to rx buffer */
while(len--) {
p_s_rxdata[p_s_rxpos++] = flip[*data++];
int media_types[32];
uint8_t payload_types[32];
int payloads = 0;
+ int media_type;
interface = getinterfacebyname(inst->interface_name);
if (!interface) {
}
message_put(message);
-#if 0
-issues:
-- send tones that are clocked with timer, unless data is received from bridge
/* send progress, if tones are available and if we don't bridge */
if (!p_s_rtp_bridge && interface->is_tones == IS_YES) {
char sdp_str[256];
unsigned char payload_type;
PDEBUG(DEBUG_SIP, "Connecting audio, since we have tones available\n");
- payload_type = (options.law=='a') ? RTP_PT_ALAW : RTP_PT_ULAW;
+ media_type = (options.law=='a') ? MEDIA_TYPE_ALAW : MEDIA_TYPE_ULAW;
+ payload_type = (options.law=='a') ? PAYLOAD_TYPE_ALAW : PAYLOAD_TYPE_ULAW;
/* open local RTP peer (if not bridging) */
if (rtp_connect() < 0) {
nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, 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, payload_type, payload_type, payload_type2name(payload_type));
+ , inet_ntoa(ia), inet_ntoa(ia), p_s_rtp_port_local, payload_type, payload_type, media_type2name(media_type));
PDEBUG(DEBUG_SIP, "Using SDP response: %s\n", sdp_str);
nua_respond(p_s_handle, SIP_183_SESSION_PROGRESS,
NUTAG_MEDIA_ENABLE(0),
SIPTAG_CONTENT_TYPE_STR("application/sdp"),
SIPTAG_PAYLOAD_STR(sdp_str), TAG_END());
- new_state(PORT_STATE_CONNECT);
sip_trace_header(this, "RESPOND", DIRECTION_OUT);
add_trace("respond", "value", "183 SESSION PROGRESS");
add_trace("reason", NULL, "audio available");
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);
+ add_trace("rtp", "payload", "%s:%d", media_type2name(media_type), payload_type);
end_trace();
}
-#endif
}
void Psip::i_bye(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tagss[])
PDEBUG(DEBUG_SIP, "SIP interface created (inst=%p)\n", inst);
+ any_sip_interface = 1;
+
return 0;
}
interface->sip_inst = NULL;
PDEBUG(DEBUG_SIP, "SIP interface removed\n");
+
+ /* check if there is any other SIP interface left */
+ interface = interface_first;
+ while (interface) {
+ if (interface->sip_inst)
+ break;
+ interface = interface->next;
+ }
+ if (!interface)
+ any_sip_interface = 0;
}
extern su_log_t su_log_default[];
return 0;
}
+
+/*
+ * generate audio, if no data is received from bridge
+ */
+
+void Psip::set_tone(const char *dir, const char *tone)
+{
+ Port::set_tone(dir, tone);
+
+ update_load();
+}
+
+void Psip::update_load(void)
+{
+ /* don't trigger load event if event already active */
+ if (p_s_loadtimer.active)
+ return;
+
+ /* don't start timer if ... */
+ if (!p_tone_name[0])
+ return;
+
+ p_s_next_tv_sec = 0;
+ schedule_timer(&p_s_loadtimer, 0, 0); /* no delay the first time */
+}
+
+static int load_timer(struct lcr_timer *timer, void *instance, int index)
+{
+ class Psip *psip = (class Psip *)instance;
+
+ /* stop timer if ... */
+ if (!psip->p_tone_name[0])
+ return 0;
+
+ psip->load_tx();
+
+ return 0;
+}
+
+#define SEND_SIP_LEN 160
+
+void Psip::load_tx(void)
+{
+ int diff;
+ struct timeval current_time;
+ int tosend = SEND_SIP_LEN, i;
+ unsigned char buf[SEND_SIP_LEN], *p = buf;
+
+ /* get elapsed */
+ gettimeofday(¤t_time, NULL);
+ if (!p_s_next_tv_sec) {
+ /* if timer expired the first time, set next expected timeout 160 samples in advance */
+ p_s_next_tv_sec = current_time.tv_sec;
+ p_s_next_tv_usec = current_time.tv_usec + SEND_SIP_LEN * 125;
+ if (p_s_next_tv_usec >= 1000000) {
+ p_s_next_tv_usec -= 1000000;
+ p_s_next_tv_sec++;
+ }
+ schedule_timer(&p_s_loadtimer, 0, SEND_SIP_LEN * 125);
+ } else {
+ diff = 1000000 * (current_time.tv_sec - p_s_next_tv_sec)
+ + (current_time.tv_usec - p_s_next_tv_usec);
+ if (diff < -SEND_SIP_LEN * 125 || diff > SEND_SIP_LEN * 125) {
+ /* if clock drifts too much, set next timeout event to current timer + 160 */
+ diff = 0;
+ p_s_next_tv_sec = current_time.tv_sec;
+ p_s_next_tv_usec = current_time.tv_usec + SEND_SIP_LEN * 125;
+ if (p_s_next_tv_usec >= 1000000) {
+ p_s_next_tv_usec -= 1000000;
+ p_s_next_tv_sec++;
+ }
+ } else {
+ /* if diff is positive, it took too long, so next timeout will be earlier */
+ p_s_next_tv_usec += SEND_SIP_LEN * 125;
+ if (p_s_next_tv_usec >= 1000000) {
+ p_s_next_tv_usec -= 1000000;
+ p_s_next_tv_sec++;
+ }
+ }
+ schedule_timer(&p_s_loadtimer, 0, SEND_SIP_LEN * 125 - diff);
+ }
+
+ /* copy tones */
+ if (p_tone_name[0]) {
+ tosend -= read_audio(p, tosend);
+ }
+ if (tosend) {
+ PERROR("buffer is not completely filled\n");
+ return;
+ }
+
+ p = buf;
+ for (i = 0; i < SEND_SIP_LEN; i++) {
+ *p = flip[*p];
+ p++;
+ }
+ /* transmit data via rtp */
+ rtp_send_frame(buf, SEND_SIP_LEN, (options.law=='a')?PAYLOAD_TYPE_ALAW:PAYLOAD_TYPE_ULAW);
+}
+