]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
dramatic jitterbuffer changes
authorAnthony Minessale <anthm@freeswitch.org>
Fri, 10 Dec 2010 23:47:24 +0000 (17:47 -0600)
committerAnthony Minessale <anthm@freeswitch.org>
Fri, 10 Dec 2010 23:47:46 +0000 (17:47 -0600)
src/include/private/switch_core_pvt.h
src/include/switch_rtp.h
src/include/switch_types.h
src/mod/applications/mod_commands/mod_commands.c
src/mod/applications/mod_dptools/mod_dptools.c
src/mod/endpoints/mod_sofia/mod_sofia.c
src/mod/endpoints/mod_sofia/sofia_glue.c
src/switch_core_io.c
src/switch_ivr.c
src/switch_rtp.c

index 13410a00ae33ed23a7c22385b9d1cd840e85071a..a565299fae1d3aa32b88044f031e175946b4d5a7 100644 (file)
@@ -31,7 +31,7 @@
  * this file does not exist!!!!
  *
  */
-
+#include "spandsp.h"
 #include "switch_profile.h"
 
 #ifndef WIN32
@@ -169,6 +169,7 @@ struct switch_core_session {
        switch_log_level_t loglevel;
        uint32_t soft_lock;
        switch_ivr_dmachine_t *dmachine;
+       plc_state_t *plc;
 };
 
 struct switch_media_bug {
index 7269a2b8d9058be7e2083e347db104e7f700be96..e3e9f665a3aa6d2c0bf86122b5f04516a7cbae6d 100644 (file)
@@ -229,7 +229,12 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_rtcp(switch_rtp_t *rtp_sessi
   \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
index c01638089fc6420cfa838d19277e9441527bcff1..d020775d4871d1448a2fc3b93e8710534db6af38 100644 (file)
@@ -790,6 +790,7 @@ typedef enum {
        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;
 
@@ -1093,6 +1094,7 @@ typedef enum {
        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;
index 9d6920e6cdae36ff8a3e2ed7770b4a870d3867af..dba86e3103b0dc66754ed5e934819dc8c0851ee3 100644 (file)
@@ -2542,6 +2542,60 @@ SWITCH_STANDARD_API(uuid_simplify_function)
 }
 
 
+#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)
 {
@@ -4770,6 +4824,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
        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");
@@ -4866,6 +4922,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load)
        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");
index eaff5b9364752bc0bfbd808fab1a42f54d002101..e508486380522770c0758272b21ebe8857d01d5c 100755 (executable)
@@ -957,6 +957,17 @@ SWITCH_STANDARD_APP(redirect_function)
        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 };
@@ -1303,13 +1314,22 @@ SWITCH_STANDARD_API(strftime_api_function)
        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 {
@@ -3513,6 +3533,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load)
        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>",
index 74c4c0897e99749745ae3f4bbd25f35e99d5f52d..0e64d54a0ae30c86dd31d8a0ec00ed92b9c30606 100644 (file)
@@ -906,7 +906,7 @@ static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_f
                        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) {
 
@@ -1332,6 +1332,49 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi
                        }
                }
                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])) {
index 6416778ab0d9047249c5331b8a1fb74f8e70c477..74f5d3d764468b6445da1b23a4c48c6667dc671d 100644 (file)
@@ -3153,18 +3153,37 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f
 
                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);
+                               }
+                               
                        }
                }
 
index 6f3e65a9f490f38a75cf9a7f3141888b8a9c71dc..a2df79d1bcee619d7631e649278b78ba440c64a9 100644 (file)
@@ -235,7 +235,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi
 
        if (switch_test_flag(*frame, SFF_CNG)) {
                status = SWITCH_STATUS_SUCCESS;
-               if (!session->bugs) {
+               if (!session->bugs && !session->plc) {
                        goto done;
                }
                is_cng = 1;
@@ -303,7 +303,13 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi
                        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;
@@ -326,6 +332,23 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi
                                                                                                  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)) {
@@ -361,6 +384,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi
                                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:
@@ -383,6 +410,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi
                                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;
@@ -462,7 +494,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi
                                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) {
@@ -481,7 +512,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi
                                }
                        }
 
-
                        if (perfect || switch_buffer_inuse(session->raw_read_buffer) >= session->read_impl.decoded_bytes_per_packet) {
                                if (perfect) {
                                        enc_frame = read_frame;
@@ -810,6 +840,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess
                        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:
index 46ba4e66593322231ca28751254043c3c83e767a..6e2aeaf637530f224ea84fbb7b173f2ab5105cd5 100644 (file)
@@ -2290,7 +2290,7 @@ SWITCH_DECLARE(void) switch_ivr_delay_echo(switch_core_session_t *session, uint3
 
        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);
 
@@ -2300,7 +2300,7 @@ SWITCH_DECLARE(void) switch_ivr_delay_echo(switch_core_session_t *session, uint3
                        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))) {
index ee1c4acf5e9a5cb81d4eff6b256dba48b9f2481b..aba14b2d0a99494aa5f89518738627a815db3492 100644 (file)
@@ -1618,14 +1618,67 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_activate_stun_ping(switch_rtp_t *rtp_
        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;
@@ -2016,14 +2069,18 @@ static void do_flush(switch_rtp_t *rtp_session)
        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) {
@@ -2047,6 +2104,16 @@ static void do_flush(switch_rtp_t *rtp_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++;
@@ -2056,6 +2123,10 @@ static void do_flush(switch_rtp_t *rtp_session)
                        }
                } 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);
@@ -2104,7 +2175,9 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
                        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;
@@ -2114,14 +2187,14 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
                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;
                }
        }
@@ -2824,7 +2897,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_
                        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;
@@ -3053,6 +3126,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read_frame(switch_rtp_t *rtp
        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);