* this file does not exist!!!!
*
*/
-
+#include "spandsp.h"
#include "switch_profile.h"
#ifndef WIN32
switch_log_level_t loglevel;
uint32_t soft_lock;
switch_ivr_dmachine_t *dmachine;
+ plc_state_t *plc;
};
struct switch_media_bug {
\param queue_frames the number of frames to delay
\return SWITCH_STATUS_SUCCESS
*/
-SWITCH_DECLARE(switch_status_t) switch_rtp_activate_jitter_buffer(switch_rtp_t *rtp_session, uint32_t queue_frames);
+SWITCH_DECLARE(switch_status_t) switch_rtp_activate_jitter_buffer(switch_rtp_t *rtp_session,
+ uint32_t queue_frames,
+ uint32_t max_queue_frames,
+ uint32_t samples_per_packet, uint32_t samples_per_second);
+
+SWITCH_DECLARE(switch_status_t) switch_rtp_deactivate_jitter_buffer(switch_rtp_t *rtp_session);
/*!
\brief Set an RTP Flag
SWITCH_MESSAGE_INDICATE_T38_DESCRIPTION,
SWITCH_MESSAGE_INDICATE_UDPTL_MODE,
SWITCH_MESSAGE_INDICATE_CLEAR_PROGRESS,
+ SWITCH_MESSAGE_INDICATE_JITTER_BUFFER,
SWITCH_MESSAGE_INVALID
} switch_core_session_message_types_t;
CF_PASSTHRU_PTIME_MISMATCH,
CF_BRIDGE_NOWRITE,
CF_RECOVERED,
+ CF_JITTERBUFFER,
/* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */
CF_FLAG_MAX
} switch_channel_flag_t;
}
+#define JITTERBUFFER_SYNTAX "<uuid> [0|<min_msec>[:<max_msec>]]"
+SWITCH_STANDARD_API(uuid_jitterbuffer_function)
+{
+ char *mydata = NULL, *argv[2] = { 0 };
+ int argc = 0;
+
+ switch_status_t status = SWITCH_STATUS_FALSE;
+
+ if (zstr(cmd)) {
+ goto error;
+ }
+
+ mydata = strdup(cmd);
+ switch_assert(mydata);
+
+ argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+
+ if (argc < 2) {
+ goto error;
+ }
+ if (argv[1]) {
+ switch_core_session_message_t msg = { 0 };
+ switch_core_session_t *lsession = NULL;
+
+ msg.message_id = SWITCH_MESSAGE_INDICATE_JITTER_BUFFER;
+ msg.string_arg = argv[1];
+ msg.from = __FILE__;
+
+ if ((lsession = switch_core_session_locate(argv[0]))) {
+ status = switch_core_session_receive_message(lsession, &msg);
+ switch_core_session_rwunlock(lsession);
+ }
+ goto ok;
+ } else {
+ goto error;
+ }
+
+ error:
+ stream->write_function(stream, "-USAGE: %s\n", JITTERBUFFER_SYNTAX);
+ switch_safe_free(mydata);
+ return SWITCH_STATUS_SUCCESS;
+ ok:
+ switch_safe_free(mydata);
+
+ if (status == SWITCH_STATUS_SUCCESS) {
+ stream->write_function(stream, "+OK Success\n");
+ } else {
+ stream->write_function(stream, "-ERR Operation Failed\n");
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
#define PHONE_EVENT_SYNTAX "<uuid>"
SWITCH_STANDARD_API(uuid_phone_event_function)
{
SWITCH_ADD_API(commands_api_interface, "uuid_transfer", "Transfer a session", transfer_function, TRANSFER_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "uuid_dual_transfer", "Transfer a session and its partner", dual_transfer_function, DUAL_TRANSFER_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "uuid_simplify", "Try to cut out of a call path / attended xfer", uuid_simplify_function, SIMPLIFY_SYNTAX);
+ SWITCH_ADD_API(commands_api_interface, "uuid_jitterbuffer", "Try to cut out of a call path / attended xfer",
+ uuid_jitterbuffer_function, JITTERBUFFER_SYNTAX);
SWITCH_ADD_API(commands_api_interface, "xml_locate", "find some xml", xml_locate_function, "[root | <section> <tag> <tag_attr_name> <tag_attr_val>]");
SWITCH_ADD_API(commands_api_interface, "xml_wrap", "Wrap another api command in xml", xml_wrap_api_function, "<command> <args>");
switch_console_set_complete("add alias add");
switch_console_set_complete("add uuid_flush_dtmf ::console::list_uuid");
switch_console_set_complete("add uuid_getvar ::console::list_uuid");
switch_console_set_complete("add uuid_hold ::console::list_uuid");
+ switch_console_set_complete("add uuid_jitterbuffer ::console::list_uuid");
switch_console_set_complete("add uuid_kill ::console::list_uuid");
switch_console_set_complete("add uuid_limit_release ::console::list_uuid");
switch_console_set_complete("add uuid_loglevel ::console::list_uuid console");
switch_core_session_receive_message(session, &msg);
}
+SWITCH_STANDARD_APP(jitterbuffer_function)
+{
+ switch_core_session_message_t msg = { 0 };
+
+ /* Tell the channel to change the jitter buffer */
+ msg.from = __FILE__;
+ msg.string_arg = data;
+ msg.message_id = SWITCH_MESSAGE_INDICATE_JITTER_BUFFER;
+ switch_core_session_receive_message(session, &msg);
+}
+
SWITCH_STANDARD_APP(display_function)
{
switch_core_session_message_t msg = { 0 };
char date[80] = "";
switch_time_t thetime;
char *p;
- if (!zstr(cmd) && (p = strchr(cmd, '|'))) {
- thetime = switch_time_make(atoi(cmd), 0);
+ char *mycmd = NULL;
+
+ if (!zstr(cmd)) {
+ mycmd = strdup(cmd);
+ }
+
+ if (!zstr(mycmd) && (p = strchr(cmd, '|'))) {
+ *p++ = '\0';
+
+ thetime = switch_time_make(atol(cmd), 0);
cmd = p + 1;
} else {
thetime = switch_micro_time_now();
}
switch_time_exp_lt(&tm, thetime);
+
if (zstr(cmd)) {
switch_strftime_nocheck(date, &retsize, sizeof(date), "%Y-%m-%d %T", &tm);
} else {
SWITCH_ADD_APP(app_interface, "ivr", "Run an ivr menu", "Run an ivr menu.", ivr_application_function, "<menu_name>", SAF_NONE);
SWITCH_ADD_APP(app_interface, "redirect", "Send session redirect", "Send a redirect message to a session.", redirect_function, "<redirect_data>",
SAF_SUPPORT_NOMEDIA);
+ SWITCH_ADD_APP(app_interface, "jitterbuffer", "Send session jitterbuffer", "Send a jitterbuffer message to a session.",
+ jitterbuffer_function, "<jitterbuffer_data>", SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_APP(app_interface, "send_display", "Send session a new display", "Send session a new display.", display_function, "<text>",
SAF_SUPPORT_NOMEDIA);
SWITCH_ADD_APP(app_interface, "respond", "Send session respond", "Send a respond message to a session.", respond_function, "<respond_data>",
tech_pvt->read_frame.flags = SFF_NONE;
status = switch_rtp_zerocopy_read_frame(tech_pvt->rtp_session, &tech_pvt->read_frame, flags);
-
+
if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
if (status == SWITCH_STATUS_TIMEOUT) {
}
}
break;
+ case SWITCH_MESSAGE_INDICATE_JITTER_BUFFER:
+ {
+ if (switch_rtp_ready(tech_pvt->rtp_session)) {
+ int len, maxlen = 0, qlen = 0, maxqlen = 50;
+
+ if (msg->string_arg) {
+ char *p;
+
+ if ((len = atoi(msg->string_arg))) {
+ qlen = len / (tech_pvt->read_impl.microseconds_per_packet / 1000);
+ }
+
+ if (qlen) {
+ if ((p = strchr(msg->string_arg, ':'))) {
+ p++;
+ maxlen = atol(p);
+ }
+ }
+
+
+ if (maxlen) {
+ maxqlen = maxlen / (tech_pvt->read_impl.microseconds_per_packet / 1000);
+ }
+ }
+
+ if (qlen) {
+ if (switch_rtp_activate_jitter_buffer(tech_pvt->rtp_session, qlen, maxqlen,
+ tech_pvt->read_impl.samples_per_packet,
+ tech_pvt->read_impl.samples_per_second) == SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session),
+ SWITCH_LOG_DEBUG, "Setting Jitterbuffer to %dms (%d frames) (%d max frames)\n", len, qlen, maxqlen);
+ switch_channel_set_flag(tech_pvt->channel, CF_JITTERBUFFER);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session),
+ SWITCH_LOG_WARNING, "Error Setting Jitterbuffer to %dms (%d frames)\n", len, qlen);
+ }
+
+ } else {
+ switch_rtp_deactivate_jitter_buffer(tech_pvt->rtp_session);
+ }
+ }
+ }
+ break;
case SWITCH_MESSAGE_INDICATE_DEBUG_AUDIO:
{
if (switch_rtp_ready(tech_pvt->rtp_session) && !zstr(msg->string_array_arg[0]) && !zstr(msg->string_array_arg[1])) {
if ((val = switch_channel_get_variable(tech_pvt->channel, "jitterbuffer_msec"))) {
int len = atoi(val);
+ int maxlen = 50;
+ char *p;
- if (len < 100 || len > 1000) {
+ if ((p = strchr(val, ':'))) {
+ p++;
+ maxlen = atoi(val);
+ }
+
+ if (len < 20 || len > 10000) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR,
- "Invalid Jitterbuffer spec [%d] must be between 100 and 1000\n", len);
+ "Invalid Jitterbuffer spec [%d] must be between 20 and 10000\n", len);
} else {
- int qlen;
-
+ int qlen, maxqlen = 0;
+
qlen = len / (tech_pvt->read_impl.microseconds_per_packet / 1000);
- switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Setting Jitterbuffer to %dms (%d frames)\n", len,
- qlen);
- switch_rtp_activate_jitter_buffer(tech_pvt->rtp_session, qlen);
+ if (maxlen) {
+ maxqlen = maxlen / (tech_pvt->read_impl.microseconds_per_packet / 1000);
+ }
+
+ if (switch_rtp_activate_jitter_buffer(tech_pvt->rtp_session, qlen, maxqlen,
+ tech_pvt->read_impl.samples_per_packet,
+ tech_pvt->read_impl.samples_per_second) == SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session),
+ SWITCH_LOG_DEBUG, "Setting Jitterbuffer to %dms (%d frames)\n", len, qlen);
+ switch_channel_set_flag(tech_pvt->channel, CF_JITTERBUFFER);
+ } else {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session),
+ SWITCH_LOG_WARNING, "Error Setting Jitterbuffer to %dms (%d frames)\n", len, qlen);
+ }
+
}
}
if (switch_test_flag(*frame, SFF_CNG)) {
status = SWITCH_STATUS_SUCCESS;
- if (!session->bugs) {
+ if (!session->bugs && !session->plc) {
goto done;
}
is_cng = 1;
session->raw_read_frame.datalen = session->raw_read_frame.buflen;
if (is_cng) {
- memset(session->raw_read_frame.data, 255, read_frame->codec->implementation->decoded_bytes_per_packet);
+ if (session->plc) {
+ plc_fillin(session->plc, session->raw_read_frame.data, read_frame->codec->implementation->decoded_bytes_per_packet / 2);
+ is_cng = 0;
+ flag &= !SFF_CNG;
+ } else {
+ memset(session->raw_read_frame.data, 255, read_frame->codec->implementation->decoded_bytes_per_packet);
+ }
session->raw_read_frame.datalen = read_frame->codec->implementation->decoded_bytes_per_packet;
session->raw_read_frame.samples = session->raw_read_frame.datalen / sizeof(int16_t);
read_frame = &session->raw_read_frame;
session->read_impl.actual_samples_per_second,
session->raw_read_frame.data, &session->raw_read_frame.datalen, &session->raw_read_frame.rate,
&read_frame->flags);
+
+ if (status == SWITCH_STATUS_SUCCESS) {
+ if (switch_channel_test_flag(session->channel, CF_JITTERBUFFER) && !session->plc) {
+ session->plc = plc_init(NULL);
+ }
+
+ if (session->plc) {
+ if (switch_test_flag(read_frame, SFF_PLC)) {
+ plc_fillin(session->plc, session->raw_read_frame.data, session->raw_read_frame.datalen / 2);
+ switch_clear_flag(read_frame, SFF_PLC);
+ } else {
+ plc_rx(session->plc, session->raw_read_frame.data, session->raw_read_frame.datalen / 2);
+ }
+ }
+ }
+
+
}
if (do_resample && ((status == SWITCH_STATUS_SUCCESS) || is_cng)) {
session->raw_read_frame.seq = read_frame->seq;
session->raw_read_frame.m = read_frame->m;
session->raw_read_frame.payload = read_frame->payload;
+ session->raw_read_frame.flags = 0;
+ if (switch_test_flag(read_frame, SFF_PLC)) {
+ session->raw_read_frame.flags |= SFF_PLC;
+ }
read_frame = &session->raw_read_frame;
break;
case SWITCH_STATUS_NOOP:
session->raw_read_frame.seq = read_frame->seq;
session->raw_read_frame.m = read_frame->m;
session->raw_read_frame.payload = read_frame->payload;
+ session->raw_read_frame.flags = 0;
+ if (switch_test_flag(read_frame, SFF_PLC)) {
+ session->raw_read_frame.flags |= SFF_PLC;
+ }
+
read_frame = &session->raw_read_frame;
status = SWITCH_STATUS_SUCCESS;
break;
read_frame->datalen = session->read_resampler->to_len * 2;
read_frame->rate = session->read_resampler->to_rate;
switch_mutex_unlock(session->resample_mutex);
-
}
if (read_frame->datalen == session->read_impl.decoded_bytes_per_packet) {
}
}
-
if (perfect || switch_buffer_inuse(session->raw_read_buffer) >= session->read_impl.decoded_bytes_per_packet) {
if (perfect) {
enc_frame = read_frame;
session->raw_write_frame.ssrc = frame->ssrc;
session->raw_write_frame.seq = frame->seq;
session->raw_write_frame.payload = frame->payload;
+ session->raw_write_frame.flags = 0;
+ if (switch_test_flag(frame, SFF_PLC)) {
+ session->raw_write_frame.flags |= SFF_PLC;
+ }
+
write_frame = &session->raw_write_frame;
break;
case SWITCH_STATUS_BREAK:
qlen = delay_ms / (interval);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setting delay to %dms (%d frames)\n", delay_ms, qlen);
- jb = stfu_n_init(qlen, 0);
+ jb = stfu_n_init(qlen, qlen, read_impl.samples_per_packet, read_impl.samples_per_second);
write_frame.codec = switch_core_session_get_read_codec(session);
break;
}
- stfu_n_eat(jb, ts, read_frame->payload, read_frame->data, read_frame->datalen);
+ stfu_n_eat(jb, ts, 0, read_frame->payload, read_frame->data, read_frame->datalen);
ts += interval;
if ((jb_frame = stfu_n_read_a_frame(jb))) {
return SWITCH_STATUS_SUCCESS;
}
-SWITCH_DECLARE(switch_status_t) switch_rtp_activate_jitter_buffer(switch_rtp_t *rtp_session, uint32_t queue_frames)
+static void jb_callback(stfu_instance_t *i, void *udata)
{
+ switch_core_session_t *session = (switch_core_session_t *) udata;
+ stfu_report_t r = { 0 };
- rtp_session->jb = stfu_n_init(queue_frames, 0);
+ stfu_n_report(i, &r);
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG1,
+ "%s JB REPORT:\nlen: %u\nin: %u\nclean: %u\ngood: %u\nbad: %u\n",
+ switch_core_session_get_name(session),
+ r.qlen,
+ r.packet_in_count,
+ r.clean_count,
+ r.consecutive_good_count,
+ r.consecutive_bad_count
+ );
+
+}
+
+SWITCH_DECLARE(switch_status_t) switch_rtp_deactivate_jitter_buffer(switch_rtp_t *rtp_session)
+{
+
+ if (!switch_rtp_ready(rtp_session) || !rtp_session->jb) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ READ_INC(rtp_session);
+ stfu_n_destroy(&rtp_session->jb);
+ READ_DEC(rtp_session);
+
return SWITCH_STATUS_SUCCESS;
}
+SWITCH_DECLARE(switch_status_t) switch_rtp_activate_jitter_buffer(switch_rtp_t *rtp_session,
+ uint32_t queue_frames,
+ uint32_t max_queue_frames,
+ uint32_t samples_per_packet,
+ uint32_t samples_per_second)
+{
+
+ if (!switch_rtp_ready(rtp_session)) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ READ_INC(rtp_session);
+ if (rtp_session->jb) {
+ stfu_n_resize(rtp_session->jb, queue_frames);
+ } else {
+ rtp_session->jb = stfu_n_init(queue_frames, max_queue_frames || 50, samples_per_packet, samples_per_second);
+ }
+ READ_DEC(rtp_session);
+
+ if (rtp_session->jb) {
+ switch_core_session_t *session = switch_core_memory_pool_get_data(rtp_session->pool, "__session");
+ stfu_n_call_me(rtp_session->jb, jb_callback, session);
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ return SWITCH_STATUS_FALSE;
+}
+
SWITCH_DECLARE(switch_status_t) switch_rtp_activate_rtcp(switch_rtp_t *rtp_session, int send_rate, switch_port_t remote_port)
{
const char *err = NULL;
switch_size_t bytes;
switch_status_t status;
- if (!switch_rtp_ready(rtp_session) || switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA)) {
+ if (!switch_rtp_ready(rtp_session) ||
+ switch_test_flag(rtp_session, SWITCH_RTP_FLAG_PROXY_MEDIA) ||
+ switch_test_flag(rtp_session, SWITCH_RTP_FLAG_VIDEO)
+ ) {
return;
}
READ_INC(rtp_session);
if (switch_rtp_ready(rtp_session)) {
-
+ uint32_t flushed = 0;
+
if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_DEBUG_RTP_READ)) {
switch_core_session_t *session = switch_core_memory_pool_get_data(rtp_session->pool, "__session");
if (!session) {
bytes = sizeof(rtp_msg_t);
status = switch_socket_recvfrom(rtp_session->from_addr, rtp_session->sock_input, 0, (void *) &rtp_session->recv_msg, &bytes);
if (bytes) {
+
+ flushed++;
+
+ if (rtp_session->jb) {
+ stfu_n_eat(rtp_session->jb, ntohl(rtp_session->recv_msg.header.ts),
+ ntohs((uint16_t) rtp_session->recv_msg.header.seq),
+ rtp_session->recv_msg.header.pt,
+ rtp_session->recv_msg.body, bytes - rtp_header_len);
+ }
+
rtp_session->stats.inbound.raw_bytes += bytes;
rtp_session->stats.inbound.flush_packet_count++;
rtp_session->stats.inbound.packet_count++;
}
} while (bytes > 0);
+ if (rtp_session->jb && flushed) {
+ stfu_n_sync(rtp_session->jb, flushed);
+ }
+
if (was_blocking && switch_rtp_ready(rtp_session)) {
switch_clear_flag_locked(rtp_session, SWITCH_RTP_FLAG_NOBLOCK);
switch_socket_opt_set(rtp_session->sock_input, SWITCH_SO_NONBLOCK, FALSE);
stfu_n_reset(rtp_session->jb);
}
- stfu_n_eat(rtp_session->jb, ntohl(rtp_session->recv_msg.header.ts), rtp_session->recv_msg.header.pt,
+ stfu_n_eat(rtp_session->jb, ntohl(rtp_session->recv_msg.header.ts),
+ ntohs((uint16_t) rtp_session->recv_msg.header.seq),
+ rtp_session->recv_msg.header.pt,
rtp_session->recv_msg.body, *bytes - rtp_header_len);
*bytes = 0;
status = SWITCH_STATUS_FALSE;
if ((jb_frame = stfu_n_read_a_frame(rtp_session->jb))) {
memcpy(rtp_session->recv_msg.body, jb_frame->data, jb_frame->dlen);
if (jb_frame->plc) {
- *flags |= SFF_PLC;
+ (*flags) |= SFF_PLC;
} else {
rtp_session->stats.inbound.jb_packet_count++;
}
*bytes = jb_frame->dlen + rtp_header_len;
rtp_session->recv_msg.header.ts = htonl(jb_frame->ts);
rtp_session->recv_msg.header.pt = jb_frame->pt;
-
+ rtp_session->recv_msg.header.seq = htons((uint16_t)jb_frame->seq);
status = SWITCH_STATUS_SUCCESS;
}
}
if ((poll_status = switch_poll(rtp_session->read_pollfd, 1, &fdr, 0)) == SWITCH_STATUS_SUCCESS) {
goto recvfrom;
}
-
+
memset(data, 0, 2);
data[0] = 65;
rtp_session->recv_msg.header.pt = (uint32_t) rtp_session->cng_pt ? rtp_session->cng_pt : SWITCH_RTP_CNG_PAYLOAD;
frame->packet = &rtp_session->recv_msg;
frame->packetlen = bytes;
frame->source = __FILE__;
+
switch_set_flag(frame, SFF_RAW_RTP);
if (frame->payload == rtp_session->recv_te) {
switch_set_flag(frame, SFF_RFC2833);