Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 37292) +++ channels/chan_sip.c (working copy) @@ -493,6 +493,7 @@ /* Global settings only apply to the channel */ static int global_rtautoclear; static int global_notifyringing; /*!< Send notifications on ringing */ +static int global_framems = 0; /*!< Default audio payload in ms */ static int global_alwaysauthreject; /*!< Send 401 Unauthorized for all failing requests */ static int srvlookup; /*!< SRV Lookup on or off. Default is off, RFC behavior is on */ static int pedanticsipchecking; /*!< Extra checking ? Default off */ @@ -953,6 +954,7 @@ struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */ struct sip_pvt *next; /*!< Next dialog in chain */ struct sip_invite_param *options; /*!< Options for INVITE */ + int framems; /*!< Packetization */ } *iflist = NULL; #define FLAG_RESPONSE (1 << 0) @@ -1000,6 +1002,7 @@ enum transfermodes allowtransfer; /*! SIP Refer restriction scheme */ struct ast_ha *ha; /*!< ACL setting */ struct ast_variable *chanvars; /*!< Variables to set for channel created by user */ + int framems; /*!< Audio payload Packetization */ int maxcallbitrate; /*!< Maximum Bitrate for a video call */ }; @@ -1061,6 +1064,7 @@ struct ast_variable *chanvars; /*!< Variables to set for channel created by user */ struct sip_pvt *mwipvt; /*!< Subscription for MWI */ int lastmsg; + int framems; /* Packetization */ }; @@ -2524,6 +2528,11 @@ ast_log(LOG_DEBUG, "Setting NAT on UDPTL to %s\n", natflags ? "On" : "Off"); ast_udptl_setnat(r->udptl, natflags); } + r->framems = peer->framems; + /* Set Frame packetization, use default if it doesn't exist. */ + if (r->rtp && r->framems) { + ast_rtp_set_framems(r->rtp, r->framems); + } ast_string_field_set(r, peername, peer->username); ast_string_field_set(r, authname, peer->username); ast_string_field_set(r, username, peer->username); @@ -3684,6 +3693,7 @@ i->owner = tmp; ast_mutex_lock(&usecnt_lock); usecnt++; + ast_mutex_unlock(&usecnt_lock); ast_copy_string(tmp->context, i->context, sizeof(tmp->context)); ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten)); @@ -5577,7 +5587,11 @@ ast_build_string(a_buf, a_size, "a=fmtp:%d annexb=no\r\n", rtp_code); } else if (codec == AST_FORMAT_ILBC) { /* Add information about us using only 20 ms packetization */ - ast_build_string(a_buf, a_size, "a=fmtp:%d mode=20\r\n", rtp_code); + if ((p->framems == 20) || (p->framems == 30)) { + ast_build_string(a_buf, a_size, "a=fmtp:%d mode=%d\r\n", rtp_code, p->framems); + } else { + ast_build_string(a_buf, a_size, "a=fmtp:%d mode=20\r\n", rtp_code); + } } } @@ -7892,6 +7906,12 @@ ASTOBJ_UNREF(peer, sip_destroy_peer); } if (peer) { + p->framems = peer->framems; + /* Set Frame packetization, use default if it doesn't exist. */ + if (p->rtp && p->framems) { + ast_rtp_set_framems(p->rtp, p->framems); + } + if (!ast_test_flag(&peer->flags[1], SIP_PAGE2_DYNAMIC)) { ast_log(LOG_ERROR, "Peer '%s' is trying to register, but not configured as host=dynamic\n", peer->name); } else { @@ -8622,6 +8642,12 @@ } } p->prefs = user->prefs; + p->framems = user->framems; + /* Set Frame packetization, use default if it doesn't exist. */ + if (p->rtp && p->framems) { + ast_rtp_set_framems(p->rtp, p->framems); + } + /* replace callerid if rpid found, and not restricted */ if (!ast_strlen_zero(rpid_num) && ast_test_flag(&p->flags[0], SIP_TRUSTRPID)) { char *tmp; @@ -8729,6 +8755,12 @@ peer = find_peer(NULL, &p->recv, 1); if (peer) { + p->framems = peer->framems; + /* Set Frame packetization, use default if it doesn't exist. */ + if (p->rtp && p->framems) { + ast_rtp_set_framems(p->rtp, p->framems); + } + if (debug) ast_verbose("Found peer '%s'\n", peer->name); @@ -9485,6 +9517,7 @@ if (!codec) break; ast_cli(fd, "%s", ast_getformatname(codec)); + ast_cli(fd, ":%d", pref->framing[x]); if (x < 31 && ast_codec_pref_index(pref, x + 1)) ast_cli(fd, ","); } @@ -9683,6 +9716,7 @@ ast_cli(fd, "%s\n",status); ast_cli(fd, " Useragent : %s\n", peer->useragent); ast_cli(fd, " Reg. Contact : %s\n", peer->fullcontact); + ast_cli(fd, " Packetization: %d\n", peer->framems); if (peer->chanvars) { ast_cli(fd, " Variables :\n"); for (v = peer->chanvars ; v ; v = v->next) @@ -14997,6 +15031,7 @@ if (!(user = ast_calloc(1, sizeof(*user)))) return NULL; + user->framems = global_framems; suserobjs++; ASTOBJ_INIT(user); ast_copy_string(user->name, name, sizeof(user->name)); @@ -15065,6 +15100,8 @@ ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 1); } else if (!strcasecmp(v->name, "disallow")) { ast_parse_allow_disallow(&user->prefs, &user->capability, v->value, 0); + } else if (!strcasecmp(v->name, "packetization")) { + user->framems = atoi(v->value); } else if (!strcasecmp(v->name, "callingpres")) { user->callingpres = ast_parse_caller_presentation(v->value); if (user->callingpres == -1) @@ -15122,6 +15159,7 @@ peer->rtptimeout = global_rtptimeout; peer->rtpholdtimeout = global_rtpholdtimeout; peer->rtpkeepalive = global_rtpkeepalive; + peer->framems = global_framems; peer->allowtransfer = global_allowtransfer; strcpy(peer->vmexten, default_vmexten); peer->secret[0] = '\0'; @@ -15146,6 +15184,7 @@ if (!(peer = ast_calloc(1, sizeof(*peer)))) return NULL; + peer->framems = global_framems; apeerobjs++; ASTOBJ_INIT(peer); set_peer_defaults(peer); @@ -15241,6 +15280,8 @@ ast_set2_flag(&peer->flags[0], ast_true(v->value), SIP_USEREQPHONE); } else if (!strcasecmp(v->name, "fromuser")) { ast_copy_string(peer->fromuser, v->value, sizeof(peer->fromuser)); + } else if (!strcasecmp(v->name, "packetization")) { + peer->framems = atoi(v->value); } else if (!strcasecmp(v->name, "host") || !strcasecmp(v->name, "outboundproxy")) { if (!strcasecmp(v->value, "dynamic")) { if (!strcasecmp(v->name, "outboundproxy") || obproxyfound) { @@ -15544,6 +15585,8 @@ ast_copy_string(global_useragent, v->value, sizeof(global_useragent)); if (option_debug) ast_log(LOG_DEBUG, "Setting SIP channel User-Agent Name to %s\n", global_useragent); + } else if (!strcasecmp(v->name, "packetization")) { + global_framems = atoi(v->value); } else if (!strcasecmp(v->name, "allowtransfer")) { global_allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED; } else if (!strcasecmp(v->name, "rtcachefriends")) { Index: include/asterisk/rtp.h =================================================================== --- include/asterisk/rtp.h (revision 37292) +++ include/asterisk/rtp.h (working copy) @@ -185,6 +185,8 @@ int ast_rtp_reload(void); +int ast_rtp_set_framems(struct ast_rtp *rtp, int ms); + #if defined(__cplusplus) || defined(c_plusplus) } #endif Index: include/asterisk/frame.h =================================================================== --- include/asterisk/frame.h (revision 37292) +++ include/asterisk/frame.h (working copy) @@ -35,6 +35,7 @@ struct ast_codec_pref { char order[32]; + int framing[32]; }; /*! \page Def_Frame AST Multimedia and signalling frames @@ -270,6 +271,7 @@ }; #define AST_SMOOTHER_FLAG_G729 (1 << 0) +#define AST_SMOOTHER_FLAG_BE (1 << 1) /* Option identifiers and flags */ #define AST_OPTION_FLAG_REQUEST 0 @@ -352,6 +354,12 @@ */ void ast_frfree(struct ast_frame *fr); +/*! \brief Finds the best smoother + * \param format + * \return Returns a AST_FORMAT_LIST structure + */ +struct ast_format_list *lookup_smoother_codec(int format, int *ms, int *len, int RTP_MTU); + /*! \brief Copies a frame * \param fr frame to act upon * Take a frame, and if it's not been malloc'd, make a malloc'd copy @@ -440,6 +448,7 @@ struct ast_format_list *ast_get_format_list(size_t *size); struct ast_smoother *ast_smoother_new(int bytes); void ast_smoother_set_flags(struct ast_smoother *smoother, int flags); +int ast_smoother_test_flag(struct ast_smoother *s, int flag); int ast_smoother_get_flags(struct ast_smoother *smoother); void ast_smoother_free(struct ast_smoother *s); void ast_smoother_reset(struct ast_smoother *s, int bytes); @@ -480,7 +489,7 @@ /*! \brief Append a audio codec to a preference list, removing it first if it was already there */ -int ast_codec_pref_append(struct ast_codec_pref *pref, int format); +int ast_codec_pref_append(struct ast_codec_pref *pref, int format, int framing); /*! \brief Select the best audio format according to preference list from supplied options. If "find_best" is non-zero then if nothing is found, the "Best" format of Index: rtp.c =================================================================== --- rtp.c (revision 37292) +++ rtp.c (working copy) @@ -135,6 +135,8 @@ unsigned int dtmfduration; int nat; unsigned int flags; + unsigned int framems; /*!< Codec packetization */ + unsigned int rtplen; struct sockaddr_in us; /*!< Socket representation of the local endpoint. */ struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */ struct timeval rxcore; @@ -485,6 +487,15 @@ *lsw = frac; } +struct rtp_codec_table { + int format; + int len; + int defaultms; + int increment; + unsigned int flags; +}; + + int ast_rtp_fd(struct ast_rtp *rtp) { return rtp->s; @@ -526,6 +537,20 @@ ast_set2_flag(rtp, dtmf ? 1 : 0, FLAG_HAS_DTMF); } +int ast_rtp_set_framems(struct ast_rtp *rtp, int ms) +{ + if (ms) { + if (rtp->smoother) { + ast_smoother_free(rtp->smoother); + rtp->smoother = NULL; + } + + rtp->framems = ms; + } + + return rtp->framems; +} + static struct ast_frame *send_dtmf(struct ast_rtp *rtp) { char iabuf[INET_ADDRSTRLEN]; @@ -2346,98 +2371,43 @@ } - switch(subclass) { - case AST_FORMAT_SLINEAR: - if (!rtp->smoother) { - rtp->smoother = ast_smoother_new(320); + if (!rtp->smoother) { + struct rtp_codec_table *ent; + int ms = rtp->framems; + int len; + + if ((ent = lookup_smoother_codec(subclass, &rtp->framems, &len, RTP_MTU))) { + + if (rtp->framems != ms) { + ast_log(LOG_DEBUG, "Had to change frame MS from %d to %d\n", ms, rtp->framems); + } + + if (!(rtp->smoother = ast_smoother_new(len))) { + ast_log(LOG_WARNING, "Unable to create smoother ms: %d len: %d:(\n", rtp->framems, len); + return -1; + } + + if (ent->flags) { + ast_smoother_set_flags(rtp->smoother, ent->flags); + } + + ast_log(LOG_DEBUG, "Able to create smoother :) ms: %d len %d\n", rtp->framems, len); } - if (!rtp->smoother) { - ast_log(LOG_WARNING, "Unable to create smoother :(\n"); - return -1; + } + + if (rtp->smoother) { + if (ast_smoother_test_flag(rtp->smoother, AST_SMOOTHER_FLAG_BE)) { + ast_smoother_feed_be(rtp->smoother, _f); + } else { + ast_smoother_feed(rtp->smoother, _f); } - ast_smoother_feed_be(rtp->smoother, _f); - - while((f = ast_smoother_read(rtp->smoother))) + + while((f = ast_smoother_read(rtp->smoother))) { ast_rtp_raw_write(rtp, f, codec); - break; - case AST_FORMAT_ULAW: - case AST_FORMAT_ALAW: - if (!rtp->smoother) { - rtp->smoother = ast_smoother_new(160); } - if (!rtp->smoother) { - ast_log(LOG_WARNING, "Unable to create smoother :(\n"); - return -1; - } - ast_smoother_feed(rtp->smoother, _f); - - while((f = ast_smoother_read(rtp->smoother))) - ast_rtp_raw_write(rtp, f, codec); - break; - case AST_FORMAT_ADPCM: - case AST_FORMAT_G726: - if (!rtp->smoother) { - rtp->smoother = ast_smoother_new(80); - } - if (!rtp->smoother) { - ast_log(LOG_WARNING, "Unable to create smoother :(\n"); - return -1; - } - ast_smoother_feed(rtp->smoother, _f); - - while((f = ast_smoother_read(rtp->smoother))) - ast_rtp_raw_write(rtp, f, codec); - break; - case AST_FORMAT_G729A: - if (!rtp->smoother) { - rtp->smoother = ast_smoother_new(20); - if (rtp->smoother) - ast_smoother_set_flags(rtp->smoother, AST_SMOOTHER_FLAG_G729); - } - if (!rtp->smoother) { - ast_log(LOG_WARNING, "Unable to create g729 smoother :(\n"); - return -1; - } - ast_smoother_feed(rtp->smoother, _f); - - while((f = ast_smoother_read(rtp->smoother))) - ast_rtp_raw_write(rtp, f, codec); - break; - case AST_FORMAT_GSM: - if (!rtp->smoother) { - rtp->smoother = ast_smoother_new(33); - } - if (!rtp->smoother) { - ast_log(LOG_WARNING, "Unable to create GSM smoother :(\n"); - return -1; - } - ast_smoother_feed(rtp->smoother, _f); - while((f = ast_smoother_read(rtp->smoother))) - ast_rtp_raw_write(rtp, f, codec); - break; - case AST_FORMAT_ILBC: - if (!rtp->smoother) { - rtp->smoother = ast_smoother_new(50); - } - if (!rtp->smoother) { - ast_log(LOG_WARNING, "Unable to create ILBC smoother :(\n"); - return -1; - } - ast_smoother_feed(rtp->smoother, _f); - while((f = ast_smoother_read(rtp->smoother))) - ast_rtp_raw_write(rtp, f, codec); - break; - default: - ast_log(LOG_WARNING, "Not sure about sending format %s packets\n", ast_getformatname(subclass)); - /* fall through to... */ - case AST_FORMAT_H261: - case AST_FORMAT_H263: - case AST_FORMAT_H263_PLUS: - case AST_FORMAT_H264: - case AST_FORMAT_G723_1: - case AST_FORMAT_LPC10: - case AST_FORMAT_SPEEX: - /* Don't buffer outgoing frames; send them one-per-packet: */ + } else { + /* Don't buffer outgoing frames; send them one-per-packet: */ + if (_f->offset < hdrlen) { f = ast_frdup(_f); } else { @@ -2445,7 +2415,7 @@ } ast_rtp_raw_write(rtp, f, codec); } - + return 0; } Index: frame.c =================================================================== --- frame.c (revision 37292) +++ frame.c (working copy) @@ -80,18 +80,33 @@ int bits; /*!< bitmask value */ char *name; /*!< short name */ char *desc; /*!< Description */ + int fr_len; /*!< Default packetization in bytes */ + int fr_ms; /*!< Default packetization in ms */ + int fr_inc; /*!< Framing increment in ms */ + unsigned int flags; /*!< Special smoother requirements */ } AST_FORMAT_LIST[] = { /*!< Bit number: comment - Bit numbers are hard coded in show_codec() */ - { 1, AST_FORMAT_G723_1 , "g723" , "G.723.1"}, /*!< 1: codec_g723_1.c */ - { 1, AST_FORMAT_GSM, "gsm" , "GSM"}, /*!< 2: codec_gsm.c */ - { 1, AST_FORMAT_ULAW, "ulaw", "G.711 u-law" }, /*!< 3: codec_ulaw.c */ - { 1, AST_FORMAT_ALAW, "alaw", "G.711 A-law" }, /*!< 4: codec_alaw.c */ - { 1, AST_FORMAT_G726, "g726", "G.726" }, /*!< 5: codec_g726.c */ - { 1, AST_FORMAT_ADPCM, "adpcm" , "ADPCM"}, /*!< 6: codec_adpcm.c */ - { 1, AST_FORMAT_SLINEAR, "slin", "16 bit Signed Linear PCM"}, /*!< 7 */ - { 1, AST_FORMAT_LPC10, "lpc10", "LPC10" }, /*!< 8: codec_lpc10.c */ - { 1, AST_FORMAT_G729A, "g729", "G.729A" }, /*!< 9: Binary commercial distribution */ - { 1, AST_FORMAT_SPEEX, "speex", "SpeeX" }, /*!< 10: codec_speex.c */ - { 1, AST_FORMAT_ILBC, "ilbc", "iLBC"}, /*!< 11: codec_ilbc.c */ + /*!< 1: codec_g723_1.c */ + { 1, AST_FORMAT_G723_1 , "g723" , "G.723.1"}, + /*!< 2: codec_gsm.c */ + { 1, AST_FORMAT_GSM, "gsm" , "GSM", 33, 20, 20}, + /*!< 3: codec_ulaw.c */ + { 1, AST_FORMAT_ULAW, "ulaw", "G.711 u-law", 80, 20 ,10 }, + /*!< 4: codec_alaw.c */ + { 1, AST_FORMAT_ALAW, "alaw", "G.711 A-law", 80, 20 ,10 }, + /*!< 5: codec_g726.c */ + { 1, AST_FORMAT_G726, "g726", "G.726" }, + /*!< 6: codec_adpcm.c */ + { 1, AST_FORMAT_ADPCM, "adpcm" , "ADPCM"}, + /*!< 7 */ + { 1, AST_FORMAT_SLINEAR, "slin", "16 bit Signed Linear PCM", 160, 20, 10, AST_SMOOTHER_FLAG_BE}, + /*!< 8: codec_lpc10.c */ + { 1, AST_FORMAT_LPC10, "lpc10", "LPC10" }, + /*!< 9: Binary commercial distribution */ + { 1, AST_FORMAT_G729A, "g729", "G.729A", 10, 20, 10, AST_SMOOTHER_FLAG_G729 }, + /*!< 10: codec_speex.c */ + { 1, AST_FORMAT_SPEEX, "speex", "SpeeX" }, + /*!< 11: codec_ilbc.c */ + { 1, AST_FORMAT_ILBC, "ilbc", "iLBC", 50, 30, 30}, { 0, 0, "nothing", "undefined" }, { 0, 0, "nothing", "undefined" }, { 0, 0, "nothing", "undefined" }, @@ -111,6 +126,32 @@ struct ast_frame ast_null_frame = { AST_FRAME_NULL, }; +struct ast_format_list *lookup_smoother_codec(int format, int *ms, int *len, int RTP_MTU) +{ + int x; + int res; + struct ast_format_list *ent = NULL; + + *len = 0; + for(x = 0 ; AST_FORMAT_LIST[x].bits ; x++) { + if (AST_FORMAT_LIST[x].bits == format) { + ent = &AST_FORMAT_LIST[x]; + if (! *ms) { + *ms = ent->fr_ms; + } + while((res = (*ms % ent->fr_inc))) { + (*ms)++; + } + while((*len = (*ms / ent->fr_inc) * ent->fr_len) > RTP_MTU) { + *ms -= ent->fr_inc; + } + break; + } + } + + return ent; +} + void ast_smoother_reset(struct ast_smoother *s, int size) { memset(s, 0, sizeof(*s)); @@ -137,6 +178,11 @@ s->flags = flags; } +int ast_smoother_test_flag(struct ast_smoother *s, int flag) +{ + return (s->flags & flag); +} + int __ast_smoother_feed(struct ast_smoother *s, struct ast_frame *f, int swap) { if (f->frametype != AST_FRAME_VOICE) { @@ -970,7 +1016,7 @@ { struct ast_codec_pref oldorder; int x, y = 0; - int slot; + int slot, size; if(!pref->order[0]) return; @@ -980,16 +1026,19 @@ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) { slot = oldorder.order[x]; + size = oldorder.framing[x]; if(! slot) break; - if(AST_FORMAT_LIST[slot-1].bits != format) + if(AST_FORMAT_LIST[slot-1].bits != format){ + pref->framing[y] = size; pref->order[y++] = slot; + } } } /*! \brief Append codec to list */ -int ast_codec_pref_append(struct ast_codec_pref *pref, int format) +int ast_codec_pref_append(struct ast_codec_pref *pref, int format, int framing) { int x, newindex = -1; @@ -1006,6 +1055,7 @@ for (x = 0; x < sizeof(AST_FORMAT_LIST) / sizeof(AST_FORMAT_LIST[0]); x++) { if(!pref->order[x]) { pref->order[x] = newindex; + pref->framing[x] = framing; break; } } @@ -1043,19 +1093,39 @@ { char *parse; char *this; - int format; + char *cdc; + char *tokb; + char *frtmp=NULL; + char this_cp[32]; + int format, framing; parse = ast_strdupa(list); while ((this = strsep(&parse, ","))) { - if (!(format = ast_getformatbyname(this))) { + + strncpy(this_cp,this,sizeof(this_cp)-1); + this_cp[sizeof(this_cp)]=0; + + cdc=strtok_r(this_cp, ":",&tokb); + frtmp=strtok_r(NULL, ":",&tokb); + + if (frtmp){ + framing=atoi(frtmp); + } else { + strncpy(cdc,this,sizeof(this)-1); + cdc[sizeof(this_cp)]=0; + framing=20; + } + + if (!(format = ast_getformatbyname(cdc))) { ast_log(LOG_WARNING, "Cannot %s unknown format '%s'\n", allowing ? "allow" : "disallow", this); continue; } + if (mask) { - if (allowing) + if (allowing){ *mask |= format; - else + } else *mask &= ~format; } @@ -1064,9 +1134,11 @@ */ if (pref && (format & AST_FORMAT_AUDIO_MASK)) { if (strcasecmp(this, "all")) { - if (allowing) - ast_codec_pref_append(pref, format); - else + if (allowing) { + ast_codec_pref_append(pref, format, framing); + ast_log(LOG_WARNING, "Setting format %d to framing %d\n", format, framing); + pref->framing[format] = framing; + } else ast_codec_pref_remove(pref, format); } else if (!allowing) { memset(pref, 0, sizeof(*pref)); Index: configs/sip.conf.sample =================================================================== --- configs/sip.conf.sample (revision 37292) +++ configs/sip.conf.sample (working copy) @@ -101,6 +101,7 @@ ;compactheaders = yes ; send compact sip headers. ; +;packetization = 20 ; The global size in ms of the rtp packets ;videosupport=yes ; Turn on support for SIP video ;maxcallbitrate=384 ; Maximum bitrate for video calls (default 384 kb/s) ; Videosupport and maxcallbitrate is settable @@ -528,8 +529,8 @@ ;disallow=all ;allow=ulaw ; dtmfmode=inband only works with ulaw or alaw! ;progressinband=no ; Polycom phones don't work properly with "never" +;packetization = 20 ; the size in ms of the rtp packets - ;[pingtel] ;type=friend ;secret=blah