# # Patch managed by http://www.holgerschurig.de/patcher.html # --- asterisk/rtp.c~jitterbuf-rtp-sip.patch +++ asterisk/rtp.c @@ -67,6 +67,62 @@ #define FLAG_3389_WARNING (1 << 0) + +/* Slav - some static jitterbuffer stuff */ +#ifdef USE_JB +#include "jitterbuf.h" +#include +#include +#include + + +/* Macros for JB logs */ +/*#define jb_verbose(...) ast_verbose(VERBOSE_PREFIX_3 " ***[RTP JB LOG]*** " __VA_ARGS__)*/ +#define jb_verbose(...) if(1){\ + char tmp[192];\ + char msg[128];\ + snprintf(msg, sizeof(msg), VERBOSE_PREFIX_3 "***[RTP JB LOG]*** " __VA_ARGS__);\ + ast_verbose("%s\n", term_color(tmp, msg, COLOR_BRGREEN, 0, sizeof(tmp)));} + +/* Macros for the frame log files */ +#define jb_framelog(...) if(rtp->jb_logfile) {fprintf(rtp->jb_logfile, __VA_ARGS__); fflush(rtp->jb_logfile);} + +/* Macros for getting string representation of the jb type */ +static const char *jb_types[] = {"JB_TYPE_CONTROL", "JB_TYPE_VOICE", "JB_TYPE_VIDEO", "JB_TYPE_SILENCE"}; +#define jb_type(type) ((type >= 0 && type <= 3) ? jb_types[type] : "UNKNOWN") + +/* Counter for the frame log files */ +static int rtp_framelog_counter = 0; + +/* Counter for jb create-destroy debuging */ +static int jb_alloc_counter = 0; + +/* Scheduler context for the jitterbuffer */ +static struct sched_context *jb_sched; + +/* Scheduler thread */ +static pthread_t jb_sched_thread; + +/* Mutex for the scheduler queue synchronization */ +AST_MUTEX_DEFINE_STATIC(jb_sched_lock); + +/* Conditional variable for the scheduler queue synchronization */ +static pthread_cond_t jb_sched_flag = PTHREAD_COND_INITIALIZER; + +/* JB function prototypes */ +static void * jb_scheduler_thread(void *data); +static struct ast_frame *ast_rtp_read_real(struct ast_rtp *rtp); +static int put_in_jb(struct ast_rtp *rtp, struct ast_frame *f); +static int get_from_jb(void *data); +static void rtp_jb_clear(struct ast_rtp *rtp); +static void rtp_jb_destroy(struct ast_rtp *rtp); +static int calc_rxstamp_jb(struct ast_rtp *rtp); +static void recalc_rxcore(struct ast_rtp *rtp); +static void jb_get_timespec(struct timespec *ts, int millisec_offset); +#endif +/* End Slav */ + + struct ast_rtp { int s; char resp; @@ -104,6 +160,26 @@ int rtp_lookup_code_cache_result; int rtp_offered_from_local; struct ast_rtcp *rtcp; + /* Slav - jitterbuffer related members */ +#ifdef USE_JB + /* The jitterbuffer itself */ + jitterbuf *jb; + /* Scheduler id */ + int jbid; + /* Mutex for synchronization of the jb_put() - jb_get() operations and for destroy safety also */ + ast_mutex_t jblock; + /* Indicates that this rtp has passed ast_rtp_destroy() */ + int destroyed; + /* The pvt helper */ + struct ast_rtp_pvt_helper pvt_helper; + /* is the channel up? */ + int isup; + /* File for frame timestamp tracing */ + FILE *jb_logfile; + /* Pathname of the log file */ + char jb_logfile_pathname[PATH_MAX]; +#endif + /* End Slav */ }; struct ast_rtcp { @@ -303,6 +379,532 @@ return f; } + + + + + +/* Slav - ****************************************************** + ******************** Utility functions ********************** + *************************************************************/ + +/* Slav - calculates the time now [ms] in receivers time, e.g. counted from the time of the first frame + received (actually from the "zero" of the received frames time, which can be less than the time of + the first frame received). */ +#ifdef USE_JB +static int calc_rxstamp_jb(struct ast_rtp *rtp) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return (tv.tv_sec - rtp->rxcore.tv_sec) * 1000 + (1000000 + tv.tv_usec - rtp->rxcore.tv_usec) / 1000 - 1000; +} +#endif +/* End Slav */ + + +/* Slav - recalculates the zero of the receiver time scale, based on the time now and the last + rtp timestamp. */ +#ifdef USE_JB +static void recalc_rxcore(struct ast_rtp *rtp) +{ + unsigned int timestamp = rtp->lastrxts; + + gettimeofday(&rtp->rxcore, NULL); + rtp->rxcore.tv_sec -= timestamp / 8000; + rtp->rxcore.tv_usec -= (timestamp % 8000) * 125; + /* Round to 20ms for nice, pretty timestamps */ + rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 20000; + if(rtp->rxcore.tv_usec < 0) + { + /* Adjust appropriately if necessary */ + rtp->rxcore.tv_usec += 1000000; + rtp->rxcore.tv_sec -= 1; + } + + timestamp = (rtp->rxcore.tv_sec * 1000 + rtp->rxcore.tv_usec / 1000); + jb_framelog("JB_PUT[%p] Recalc rxcore=%u\n", rtp, timestamp); +} +#endif +/* End Slav */ + + +/* Slav - utility function - fills in the members of a timespec struct with the time now + millisec_offset + millisec_offset must be between 0 and 1000 */ +#ifdef USE_JB +static void jb_get_timespec(struct timespec *ts, int millisec_offset) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000 + millisec_offset * 1000000; + if(ts->tv_nsec >= 1000000000) + { + ts->tv_sec += 1; + ts->tv_nsec -= 1000000000; + } +} +#endif +/* End Slav */ + +/* End Slav - ************************************************** + ****************** End Utility functions ******************** + *************************************************************/ + + + + +/* Slav - ****************************************************** + ***************** Jitterbuf main functions ****************** + *************************************************************/ + +/* Slav - puts a frame into the jitterbuf */ +#ifdef USE_JB +static int put_in_jb(struct ast_rtp *rtp, struct ast_frame *f) +{ + long ts; + long now; + int type; + int len; + struct ast_frame *frame; + + /* if the call is not yet answered, don't put the frame into the jb - just let it go through + the regular way. */ + if(rtp->pvt_helper.get_chan_state(rtp->pvt_helper.pvt) != AST_STATE_UP) + { + return -1; + } + else if(!rtp->isup) + { + /* The channel is up for the very first time. */ + rtp->isup = 1; + + //recalc_rxcore(rtp); + jb_framelog("JB_PUT[%p] {now=%ld}: The call was answered.\n", rtp, (now = calc_rxstamp_jb(rtp))); + + /* lock the queue mutex */ + ast_mutex_lock(&jb_sched_lock); + + /* add a schedule for get_from_jb() to happens after 20ms */ + rtp->jbid = ast_sched_add(jb_sched, 20, get_from_jb, rtp); + + /* signal the schedule queue to wakeup the scheduler (if its waiting) */ + pthread_cond_signal(&jb_sched_flag); + + /* unlock the queue mutex */ + ast_mutex_unlock(&jb_sched_lock); + + jb_verbose("Jitterbuffer started."); + } + + /* convert the timestamp of the frame from samples to millisec */ + ts = rtp->lastrxts / 8; + + type = JB_TYPE_CONTROL; + len = 0; + + if(f->frametype == AST_FRAME_VOICE) + { + type = JB_TYPE_VOICE; + len = ast_codec_get_samples(f) / 8; + } + else if(f->frametype == AST_FRAME_CNG) + { + type = JB_TYPE_SILENCE; + } + + /* Allocate a new frame */ + frame = ast_frisolate(f); + if(frame == NULL) + { + ast_log(LOG_ERROR, "Cannot isolate frame for the jitterbuffer.\n"); + return -1; + } + + /* lock the jb mutex */ + ast_mutex_lock(&rtp->jblock); + + /* The moment now in receivers time (in millisec) */ + now = calc_rxstamp_jb(rtp); + + /* Put into the jitterbuffer. If jb_put returns JB_DROP, the frame should be dropped */ + if(jb_put(rtp->jb, frame, type, len, ts, now) == JB_DROP) + { + jb_framelog("JB_PUT[%p] {now=%ld}: Dropped %s frame with ts=%lu\n", rtp, now, jb_type(type), ts); + + /* unlock the jb mutex */ + ast_mutex_unlock(&rtp->jblock); + + /* Drop the frame - just do nothing, except of cource, freeing the isolated frame. */ + ast_frfree(frame); + } + else + { + jb_framelog("JB_PUT[%p] {now=%ld}: Queued %s frame with ts=%lu\n", rtp, now, jb_type(type), ts); + + /* unlock the jb mutex */ + ast_mutex_unlock(&rtp->jblock); + } + + return 0; +} +#endif +/* End Slav */ + + +/* Slav - gets a frame from the jitterbuf for time now */ +#ifdef USE_JB +static int get_from_jb(void *data) +{ + struct ast_rtp *rtp = (struct ast_rtp *) data; + jb_frame frame; + struct ast_frame af; + int ret; + long now; + long next; + long when; + long tmp; + struct timeval tv; + + if(rtp == NULL) + { + /* this should never happens */ + ast_log(LOG_ERROR, "Received NULL rtp data!\n"); + /* if in debug - abort asterisk */ + assert(0); + /* if no - it will crash on the next line */ + } + + /* lock the jb mutex */ + ast_mutex_lock(&rtp->jblock); + + /* check for destroy conditon */ + if(rtp->destroyed) + { + /* unlock the jb mutex */ + ast_mutex_unlock(&rtp->jblock); + + /* set jbid to -1. TODO: No sense to do this since rtp is destroyed 2 lines below */ + rtp->jbid = -1; + + /* Clear and destroy the jb and the rtp */ + rtp_jb_clear(rtp); + rtp_jb_destroy(rtp); + + /* return 0 - no reschedules of this jb should happen after this. */ + return 0; + } + + gettimeofday(&tv,NULL); + /* round up a millisecond since ast_sched_runq does; + prevents us from spinning while waiting for our now to catch up with runq's now */ + tv.tv_usec += 1000; + /* The moment now in receivers time (in millisec) */ + now = (tv.tv_sec - rtp->rxcore.tv_sec) * 1000 + (tv.tv_usec - rtp->rxcore.tv_usec) / 1000; + + /* is it time for the next frame in the jb? */ + if(now >= (next = jb_next(rtp->jb))) + { + /* get a frame from the jb for time now */ + ret = jb_get(rtp->jb, &frame, now, ast_codec_interp_len(rtp->lastrxformat)); + + /* Proceed with the frame timestamps logging while still holding the jb mutex lock */ + if(rtp->jb_logfile != NULL && rtp->pvt_helper.get_chan_state(rtp->pvt_helper.pvt) == AST_STATE_UP) + { + switch(ret) + { + case JB_OK: + jb_framelog("\tJB_GET[%p] {now=%ld}: Delivered %s frame with timestamp %ld\n", + rtp, now, jb_type(frame.type), frame.ts); + break; + case JB_INTERP: + jb_framelog("\tJB_GET[%p] {now=%ld}: Interpolated frame\n", rtp, now); + break; + case JB_DROP: + jb_framelog("\tJB_GET[%p] {now=%ld}: Dropped %s frame with timestamp %ld\n", + rtp, now, jb_type(frame.type), frame.ts); + break; + case JB_NOFRAME: + jb_framelog("\tJB_GET[%p] {now=%ld}: No frame\n", rtp, now); + break; + case JB_EMPTY: + jb_framelog("\tJB_GET[%p] {now=%ld}: Jb is empty\n", rtp, now); + break; + default: + ast_log(LOG_ERROR, "This should never happens!"); + break; + } + } + + /* unlock the jb mutex */ + ast_mutex_unlock(&rtp->jblock); + + switch(ret) + { + case JB_OK: + /* don't deliver if the channel is not up */ + if(rtp->pvt_helper.get_chan_state(rtp->pvt_helper.pvt) == AST_STATE_UP) + { + /* deliver the frame into the channel's queue (internally locks the channel mutex!) */ + rtp->pvt_helper.queue_frame(rtp->pvt_helper.pvt, frame.data); + } + ast_frfree(frame.data); + break; + case JB_INTERP: + /* don't interpolate if the channel is not up */ + if(rtp->pvt_helper.get_chan_state(rtp->pvt_helper.pvt) == AST_STATE_UP) + { + /* Create an interpolation frame */ + memset(&af, 0, sizeof(struct ast_frame)); + // TODO: Remove the unnecessary member zero initializations + af.frametype = AST_FRAME_VOICE; + af.subclass = rtp->pvt_helper.get_nativeformats(rtp->pvt_helper.pvt); + af.datalen = 0; + af.samples = frame.ms * 8; + /* Mark as non malloced, because of ast_queue_frame? */ + af.mallocd = 0; + af.src = "SIP JB interpolation"; + af.data = NULL; + // TODO: Do we need to set a delivery timeval? + af.delivery.tv_sec = rtp->rxcore.tv_sec; + af.delivery.tv_usec = rtp->rxcore.tv_usec; + af.delivery.tv_sec += next / 1000; + af.delivery.tv_usec += (next % 1000) * 1000; + af.offset=AST_FRIENDLY_OFFSET; + if(af.delivery.tv_usec >= 1000000) + { + af.delivery.tv_usec -= 1000000; + af.delivery.tv_sec += 1; + } + /* deliver the frame into the channel's queue (internally locks the channel mutex!) */ + rtp->pvt_helper.queue_frame(rtp->pvt_helper.pvt, &af); + } + break; + case JB_DROP: + /* Just free the frame */ + ast_frfree(frame.data); + break; + case JB_NOFRAME: + break; + case JB_EMPTY: + break; + default: + ast_log(LOG_ERROR, "This should never happens!"); + break; + } + } + else + { + jb_framelog("\tJB_GET[%p] {now=%ld}: now < next=%ld\n", rtp, now, next); + + /* unlock the jb mutex */ + ast_mutex_unlock(&rtp->jblock); + } + + /* Calc how many millisec remains to the next time a frame should be delivered */ + next = jb_next(rtp->jb); + when = next - now; + /* Don't use when < 2ms and > 30ms! */ + if(when < 2) + { + when = 2; + } + if(when > 30) + { + /* Obviously the jitterbuffer is confused. Use the length of the last frame. */ + tmp = ast_codec_interp_len(rtp->lastrxformat); + tmp = (tmp >= 10 && tmp <= 30) ? tmp : 20; + jb_framelog("\tJB_GET[%p] {now=%ld}: Invalid when=%ld, next=%ld. Resetting to %ld ms.\n", + rtp, now, when, next, tmp); + when = tmp; + } + + /* reschedule (adds again this schedule) */ + rtp->jbid = ast_sched_add(jb_sched, when, get_from_jb, rtp); + + return 0; +} +#endif +/* End Slav */ + + +/* Slav - called from a channel who wants a jitterbuff enabled - preforms jitterbuff initialization */ +#ifdef USE_JB +void ast_rtp_jb_enable(struct ast_rtp *rtp, struct ast_rtp_pvt_helper *pvt_helper, + struct sched_context *ex_sched, struct io_context *ex_io) +{ + jb_conf jbinfo; + + /* Shouldn't be called more than once on a given rtp! */ + assert(rtp->jb == NULL); + + /* Initialize the destroy flag */ + rtp->destroyed = 0; + + /* Initialize the jb sched id */ + rtp->jbid = -1; + + /* Create the jitterbuf */ + rtp->jb = jb_new(); + + /* Is the new jitterbuf successfuly created? */ + if(rtp->jb != NULL) + { + /* Copy the pvt_helper data. TODO: Make sure the pvt_helper members are valid */ + memcpy(&rtp->pvt_helper, pvt_helper, sizeof(struct ast_rtp_pvt_helper)); + + /* Set the maximum lenght of the jitterbuffer (in milliseconds) */ + jbinfo.max_jitterbuf = 1000; + jbinfo.resync_threshold = 1000; + jb_setconf(rtp->jb,&jbinfo); + + /* Initialize the jb mutex */ + ast_mutex_init(&rtp->jblock); + + /* lock the queue mutex */ + ast_mutex_lock(&jb_sched_lock); + + /* Create a frame log file */ + /* + snprintf(rtp->jb_logfile_pathname, sizeof(rtp->jb_logfile_pathname), + "/tmp/jb_frames_%.5d.log", rtp_framelog_counter++); + rtp->jb_logfile = fopen(rtp->jb_logfile_pathname, "w+b"); + */ + + /* Increment the jb alloc counter */ + jb_alloc_counter++; + + /* add a new schedule for get_from_jb() (scheduled each 20 ms?) */ + //rtp->jbid = ast_sched_add(jb_sched, 20, get_from_jb, rtp); + + /* signal the schedule queue to wakeup the scheduler (if its waiting) */ + //pthread_cond_signal(&jb_sched_flag); + + jb_verbose("Jitterbuffer enabled. jb_alloc=%d.", jb_alloc_counter); + + /* unlock the queue mutex */ + ast_mutex_unlock(&jb_sched_lock); + } + else + { + /* Cannot allocate memory for the jb - only log a warning and continue using the + channel without a jb */ + ast_log(LOG_WARNING, "Unable to allocate new jitterbuffer.\n"); + } +} +#endif +/* End Slav */ + + +/* Slav - removes all remaining frames from the jb and frees them */ +#ifdef USE_JB +static void rtp_jb_clear(struct ast_rtp *rtp) +{ + jb_frame frame; + + /* remove and free any remaining frames in the jb */ + while(jb_getall(rtp->jb,&frame) == JB_OK) + { + ast_frfree(frame.data); + } +} +#endif +/* End Slav */ + + +/* Slav - destroys the jitterbuf and the jb mutex and frees its rtp container */ +#ifdef USE_JB +static void rtp_jb_destroy(struct ast_rtp *rtp) +{ + /* destroy the jitterbuffer */ + jb_destroy(rtp->jb); + rtp->jb = NULL; + + /* close the framelog file */ + if(rtp->jb_logfile != NULL) + { + fseek(rtp->jb_logfile, 0, SEEK_END); + int len = ftell(rtp->jb_logfile); + fclose(rtp->jb_logfile); + rtp->jb_logfile = NULL; + + /* If there is less than 1024 bytes logged in this file - delete it */ + if(len < 1024) + { + remove(rtp->jb_logfile_pathname); + } + } + + /* Decrement the jb_alloc_counter (Called with the jb_sched_lock held) */ + jb_alloc_counter--; + + /* destroy the jb mutex */ + ast_mutex_destroy(&rtp->jblock); + + /* finally free the rtp */ + free(rtp); + + jb_verbose("Jitterbuffer destroyed. *** *** jb_alloc_counter=%d *** ***.", jb_alloc_counter); +} +#endif +/* End Slav */ + + +/* Slav - jitterbuffer scheduler thread function */ +#ifdef USE_JB +static void * jb_scheduler_thread(void *data) +{ + int when; + struct timespec abstime; + + jb_verbose("The rtp jitterbuffer scheduler thread is running."); + + while(1) + { + /* lock the queue mutex */ + ast_mutex_lock(&jb_sched_lock); + + /* get how many millisecs to wait until the first event scheduled */ + when = ast_sched_wait(jb_sched); + /* don't wait more than 1 sec. when < 0 means that the sched queue is empty */ + if(when < 0 || when > 1000) + { + when = 1000; + } + + /* wait until its time for the first schedule - can be interrupted by adding new schedule + (internally unlocks the queue mutex and locks it after the waiting is finished) */ + if(when > 0) + { + jb_get_timespec(&abstime, when); + pthread_cond_timedwait(&jb_sched_flag, &jb_sched_lock, &abstime); + } + + /* unlock the queue mutex */ + ast_mutex_unlock(&jb_sched_lock); + + /* exec all for time now */ + ast_sched_runq(jb_sched); + } + + return NULL; +} +#endif +/* End Slav */ + + +/* End Slav - ************************************************** + *************** End Jitterbuf main functions **************** + *************************************************************/ + + + + + + + static int rtpread(int *id, int fd, short events, void *cbdata) { struct ast_rtp *rtp = cbdata; @@ -383,8 +985,47 @@ } } + +/* Slav - the jitterbuf version of ast_rtp_read(). Returns AST_FRAME_NULL if the jb is used */ +#ifdef USE_JB struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) { + static struct ast_frame *frr, nf = { AST_FRAME_NULL, }; + + /* Is a jitterbuffer used for this rtp? */ + if(rtp->jb != NULL) + { + /* Read a frame from the network */ + frr = ast_rtp_read_real(rtp); + + /* Put the frame into the jb. */ + if(put_in_jb(rtp, frr) == 0) + { + return &nf; + } + + /* If put_in_jb() returns non zero (e.g. the channel is not up), deliver the frame immediately. */ + return frr; + } + + /* If not using jb, deliver the frame immediately. */ + return ast_rtp_read_real(rtp); +} +#endif +/* End Slav */ + + +/* Slav - the real ast_rtp_read() function will be used if the jitterbuffer is not used */ +#ifndef USE_JB +/* End Slav */ +struct ast_frame *ast_rtp_read(struct ast_rtp *rtp) +/* Slav - else predefine the real ast_rtp_read() func, so the jb version will be used from + all rtp channels */ +#else +static struct ast_frame *ast_rtp_read_real(struct ast_rtp *rtp) +#endif +/* End Slav */ +{ int res; struct sockaddr_in sin; socklen_t len; @@ -1008,7 +1649,39 @@ close(rtp->rtcp->s); free(rtp->rtcp); } + /* Slav - don't free the rtp here if we are using the jb - just add it to the free list. */ +#ifndef USE_JB + /* End Slav */ free(rtp); + /* Slav - set the destroy flag to the rtp, but only if a jb is enabled */ +#else + jb_verbose("ast_rtp_destroy() invoked."); + /* Does a jitterbuf is used for this rtp? */ + if(rtp->jb != NULL) + { + /* lock the jb mutex */ + ast_mutex_lock(&rtp->jblock); + + /* set the destroy flag to let get_from_jb() knows that the rtp is on the free list */ + rtp->destroyed = 1; + + /* unlock the jb mutex */ + ast_mutex_unlock(&rtp->jblock); + + /* If there was no sched added yet (the channel was never up), destroy here */ + if(rtp->jbid == -1) + { + rtp_jb_clear(rtp); + rtp_jb_destroy(rtp); + } + } + else + { + /* No - free the rtp */ + free(rtp); + } +#endif + /* End Slav */ } static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery) @@ -1755,4 +2428,25 @@ ast_cli_register(&cli_debug_ip); ast_cli_register(&cli_no_debug); ast_rtp_reload(); + + /* Slav - init the rtp jb scheduler stuff */ +#ifdef USE_JB + /* Create a scheduler context */ + jb_sched = sched_context_create(); + if(jb_sched == NULL) + { + ast_log(LOG_ERROR, "Unable to create rtp jb scheduler context.\n"); + assert(0); + } + + /* Start the scheduler thread */ + if(ast_pthread_create(&jb_sched_thread, NULL, jb_scheduler_thread, NULL) < 0) + { + ast_log(LOG_ERROR, "Unable to start rtp jb scheduler thread.\n"); + assert(0); + } + + jb_verbose("Started the rtp jitterbuffer scheduler thread."); +#endif + /* End Slav */ } --- asterisk/include/asterisk/rtp.h~jitterbuf-rtp-sip.patch +++ asterisk/include/asterisk/rtp.h @@ -25,6 +25,12 @@ extern "C" { #endif + +/* Slav - define USE_JB to enable the jitterbuffer for all rtp channels */ +#define USE_JB +/* End Slav */ + + /* Codes for RTP-specific data - not defined by our AST_FORMAT codes */ /*! DTMF (RFC2833) */ #define AST_RTP_DTMF (1 << 0) @@ -49,6 +55,33 @@ struct ast_rtp; + +/* Slav - jitterbuffer related stuff */ +#ifdef USE_JB +typedef int (*pvt_queue_frame)(void *pvt, struct ast_frame *f); +typedef int (*pvt_get_chan_state)(void *pvt); +typedef int (*pvt_get_nativeformats)(void *pvt); + +/* Defines technology independent way to access some channel functionality */ +struct ast_rtp_pvt_helper +{ + /* Pointer to the tech private structure */ + void *pvt; + /* Pointer to a function for queueing a frame into a channel */ + pvt_queue_frame queue_frame; + /* Pointer to a function, returning the current channel state */ + pvt_get_chan_state get_chan_state; + /* Pointer to a function, returning the supported from the channel formats */ + pvt_get_nativeformats get_nativeformats; +}; + +/* Enables the jitterbuff for the specified channel (and rtp). Performs jitterbuff initialization */ +void ast_rtp_jb_enable(struct ast_rtp *rtp, struct ast_rtp_pvt_helper *pvt_helper, + struct sched_context *ex_sched, struct io_context *ex_io); +#endif +/* End Slav */ + + typedef int (*ast_rtp_callback)(struct ast_rtp *rtp, struct ast_frame *f, void *data); struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode); --- asterisk/channels/chan_sip.c~jitterbuf-rtp-sip.patch +++ asterisk/channels/chan_sip.c @@ -2644,10 +2644,60 @@ snprintf(callid, len, "@%s", ast_inet_ntoa(iabuf, sizeof(iabuf), ourip)); } + +/* Slav - functions for the rtp pvt helper */ +#ifdef USE_JB +static int sip_queue_frame(void *pvt, struct ast_frame *f) +{ + struct sip_pvt *p = (struct sip_pvt *) pvt; + if(p->owner != NULL) + { + return ast_queue_frame(p->owner, f); + } + else + { + return -1; + } +} + +static int sip_get_chan_state(void *pvt) +{ + struct sip_pvt *p = (struct sip_pvt *) pvt; + if(p->owner != NULL) + { + return p->owner->_state; + } + else + { + return -1; + } +} + +static int sip_get_nativeformats(void *pvt) +{ + struct sip_pvt *p = (struct sip_pvt *) pvt; + if(p->owner != NULL) + { + return p->owner->nativeformats; + } + else + { + return -1; + } +} +#endif +/* End Slav */ + + /*--- sip_alloc: Allocate SIP_PVT structure and set defaults ---*/ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method) { struct sip_pvt *p; + /* Slav - pvt_helper fot the rtp jitterbuffer */ +#ifdef USE_JB + struct ast_rtp_pvt_helper pvt_helper; +#endif + /* End Slav */ p = malloc(sizeof(struct sip_pvt)); if (!p) @@ -2679,6 +2729,19 @@ if (sip_methods[intended_method].need_rtp) { p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); + + /* Slav - enable a jitterbuffer on the newly created rtp */ +#ifdef USE_JB + /* Init the pvt helper functions */ + pvt_helper.pvt = p; + pvt_helper.queue_frame = sip_queue_frame; + pvt_helper.get_chan_state = sip_get_chan_state; + pvt_helper.get_nativeformats = sip_get_nativeformats; + /* enable the jitterbuffer */ + ast_rtp_jb_enable(p->rtp, &pvt_helper, sched, io); +#endif + /* End Slav */ + if (videosupport) p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); if (!p->rtp) {