summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h6
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c262
-rw-r--r--tests/pjsua/scripts-sipp/uas-answer-180-multiple-fmts-support-update.xml170
-rw-r--r--tests/pjsua/scripts-sipp/uas-answer-180-multiple-fmts.xml172
-rw-r--r--tests/pjsua/scripts-sipp/uas-answer-200-multiple-fmts-support-update.xml139
-rw-r--r--tests/pjsua/scripts-sipp/uas-answer-200-multiple-fmts.xml140
6 files changed, 889 insertions, 0 deletions
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index 877e6b8e..f20b73de 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -90,6 +90,12 @@ typedef struct pjsua_call
char last_text_buf_[128]; /**< Buffer for last_text. */
+ struct {
+ pj_timer_entry reinv_timer;/**< Reinvite retry timer. */
+ pjmedia_sdp_session *new_sdp;/**< The new SDP offer. */
+ } lock_codec; /**< Data for codec locking when answer
+ contains multiple codecs. */
+
} pjsua_call;
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index 85db6d5f..f3ca5cba 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -24,6 +24,12 @@
#define THIS_FILE "pjsua_call.c"
+/* Retry interval of sending re-INVITE for locking a codec when remote
+ * SDP answer contains multiple codec, in milliseconds.
+ */
+#define LOCK_CODEC_RETRY_INTERVAL 200
+
+
/* This callback receives notification from invite session when the
* session state has changed.
*/
@@ -1513,6 +1519,13 @@ PJ_DEF(pj_status_t) pjsua_call_hangup(pjsua_call_id call_id,
return status;
}
+ /* Stop lock codec timer, if it is active */
+ if (call->lock_codec.reinv_timer.id) {
+ pjsip_endpt_cancel_timer(pjsua_var.endpt,
+ &call->lock_codec.reinv_timer);
+ call->lock_codec.reinv_timer.id = PJ_FALSE;
+ }
+
pjsip_dlg_dec_lock(dlg);
return PJ_SUCCESS;
@@ -2913,6 +2926,226 @@ PJ_DEF(pj_status_t) pjsua_call_dump( pjsua_call_id call_id,
}
+/* Timer callback to close sound device */
+static void reinv_timer_cb(pj_timer_heap_t *th,
+ pj_timer_entry *entry)
+{
+ pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
+ pjsip_dialog *dlg;
+ pjsua_call *call;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ PJ_UNUSED_ARG(th);
+
+ pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
+
+ status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
+ if (status != PJ_SUCCESS)
+ return;
+
+ /* Verify if another SDP negotiation is in progress, e.g: session timer
+ * or another re-INVITE.
+ */
+ if (call->inv==NULL || call->inv->neg==NULL ||
+ pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
+ {
+ goto on_return;
+ }
+
+ /* Verify if another SDP negotiation has been completed by comparing
+ * the SDP version.
+ */
+ {
+ const pjmedia_sdp_session *sdp;
+
+ status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &sdp);
+ if (status == PJ_SUCCESS &&
+ sdp->origin.version > call->lock_codec.new_sdp->origin.version)
+ {
+ goto on_return;
+ }
+ }
+
+ /* Create re-INVITE with the new offer */
+ status = pjsip_inv_reinvite(call->inv, NULL, call->lock_codec.new_sdp,
+ &tdata);
+ if (status == PJ_EINVALIDOP) {
+ /* Ups, let's reschedule again */
+ pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
+ call->lock_codec.reinv_timer.id = PJ_TRUE;
+ pjsip_endpt_schedule_timer(pjsua_var.endpt,
+ &call->lock_codec.reinv_timer, &delay);
+ } else if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Failed creating re-INVITE in lock codec",
+ status);
+ }
+
+ /* Send the UPDATE/re-INVITE request */
+ status = pjsip_inv_send_msg(call->inv, tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Failed sending re-INVITE in lock codec",
+ status);
+ }
+
+on_return:
+ pjsip_dlg_dec_lock(dlg);
+}
+
+
+/* Check if the specified format can be skipped in counting codecs */
+static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
+ const pj_str_t *fmt)
+{
+ unsigned pt;
+
+ pt = pj_strtoul(fmt);
+
+ /* Check for comfort noise */
+ if (pt == PJMEDIA_RTP_PT_CN)
+ return PJ_TRUE;
+
+ /* Dynamic PT, check the format name */
+ if (pt >= 96) {
+ pjmedia_sdp_attr *a;
+ pjmedia_sdp_rtpmap rtpmap;
+
+ /* Get the format name */
+ a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
+ if (a && pjmedia_sdp_attr_get_rtpmap(a, &rtpmap)==PJ_SUCCESS) {
+ /* Check for telephone-event */
+ if (pj_stricmp2(&rtpmap.enc_name, "telephone-event")==0)
+ return PJ_TRUE;
+ } else {
+ /* Invalid SDP, should not reach here */
+ pj_assert(!"SDP should have been validated!");
+ return PJ_TRUE;
+ }
+ }
+
+ return PJ_FALSE;
+}
+
+
+/* Check if remote answerer has given us more than one codecs. If so,
+ * create another offer with one codec only to lock down the codec.
+ */
+static pj_status_t lock_codec(pjsua_call *call)
+{
+ const pj_str_t st_update = {"UPDATE", 6};
+ pjsip_inv_session *inv = call->inv;
+ const pjmedia_sdp_session *local_sdp;
+ const pjmedia_sdp_session *remote_sdp;
+ const pjmedia_sdp_media *rem_m;
+ pjmedia_sdp_session *new_sdp;
+ pjmedia_sdp_media *m;
+ pjsip_tx_data *tdata;
+ unsigned i, codec_cnt = 0;
+ pj_status_t status;
+
+ if (!pjmedia_sdp_neg_was_answer_remote(inv->neg))
+ return PJ_SUCCESS;
+
+ status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
+ if (status != PJ_SUCCESS)
+ return status;
+ status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ PJ_ASSERT_RETURN(call->audio_idx>=0 &&
+ call->audio_idx < (int)remote_sdp->media_count,
+ PJ_EINVALIDOP);
+
+ rem_m = remote_sdp->media[call->audio_idx];
+
+ /* Check if media is disabled or only one format in the answer. */
+ if (rem_m->desc.port==0 || rem_m->desc.fmt_count==1)
+ return PJ_SUCCESS;
+
+ /* Count the formats in the answer. */
+ for (i=0; i<rem_m->desc.fmt_count && codec_cnt <= 1; ++i) {
+ if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[i]))
+ ++codec_cnt;
+ }
+
+ if (codec_cnt <= 1) {
+ /* Answer contains single codec. */
+ return PJ_SUCCESS;
+ }
+
+ PJ_LOG(3, (THIS_FILE, "Got answer with multiple codecs, start "
+ "updating media session to use only one codec.."));
+
+ /* Clone the offer */
+ new_sdp = pjmedia_sdp_session_clone(inv->pool_prov, local_sdp);
+ /* Note that the usage of pool_prov above is risky when locking codec
+ * delays the re-INVITE (using timer) and there are two SDP negotiations
+ * done before the re-INVITE.
+ */
+
+ /* Update the new offer so it contains only a codec. Note that formats
+ * order in the offer should have been matched to the answer, so we can
+ * just directly update the offer without looking-up the answer.
+ */
+ m = new_sdp->media[call->audio_idx];
+ codec_cnt = 0;
+ i = 0;
+ while (i < m->desc.fmt_count) {
+ pjmedia_sdp_attr *a;
+ pj_str_t *fmt = &m->desc.fmt[i];
+
+ if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
+ ++i;
+ continue;
+ }
+
+ /* Remove format */
+ a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
+ if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
+ a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
+ if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
+ pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
+ m->desc.fmt_count, i);
+ --m->desc.fmt_count;
+ }
+
+ /* Send new SDP offer via UPDATE or re-INVITE */
+ if (pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)==
+ PJSIP_DIALOG_CAP_SUPPORTED)
+ {
+ /* Create UPDATE with the new offer */
+ status = pjsip_inv_update(inv, NULL, new_sdp, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ } else {
+ /* Create re-INVITE with the new offer */
+ status = pjsip_inv_reinvite(inv, NULL, new_sdp, &tdata);
+ if (status == PJ_EINVALIDOP) {
+ /* Current INVITE transaction is pending, reschedule re-INVITE. */
+ pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
+
+ call->lock_codec.new_sdp = new_sdp;
+ pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
+ (void*)(pj_size_t)call->index,
+ &reinv_timer_cb);
+ pjsip_endpt_schedule_timer(pjsua_var.endpt,
+ &call->lock_codec.reinv_timer, &delay);
+ return PJ_SUCCESS;
+
+ } else if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ /* Send the UPDATE/re-INVITE request */
+ status = pjsip_inv_send_msg(inv, tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ return PJ_SUCCESS;
+}
+
/*
* This callback receives notification from invite session when the
* session state has changed.
@@ -2946,6 +3179,16 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
break;
case PJSIP_INV_STATE_CONFIRMED:
pj_gettimeofday(&call->conn_time);
+
+ /* Ticket #476, locking a codec in the media session. */
+ {
+ pj_status_t status;
+ status = lock_codec(call);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to lock codec", status);
+ }
+ }
+
break;
case PJSIP_INV_STATE_DISCONNECTED:
pj_gettimeofday(&call->dis_time);
@@ -2965,6 +3208,13 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv,
pjsip_get_status_text(call->last_code),
sizeof(call->last_text_buf_));
}
+
+ /* Stop lock codec timer, if it is active */
+ if (call->lock_codec.reinv_timer.id) {
+ pjsip_endpt_cancel_timer(pjsua_var.endpt,
+ &call->lock_codec.reinv_timer);
+ call->lock_codec.reinv_timer.id = PJ_FALSE;
+ }
break;
default:
call->last_code = (pjsip_status_code)
@@ -3169,6 +3419,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
pjsua_call *call;
const pjmedia_sdp_session *local_sdp;
const pjmedia_sdp_session *remote_sdp;
+ const pj_str_t st_update = {"UPDATE", 6};
PJSUA_LOCK();
@@ -3240,6 +3491,17 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv,
return;
}
+ /* Ticket #476, handle the case of early media and remote support UPDATE */
+ if (inv->state == PJSIP_INV_STATE_EARLY &&
+ pjmedia_sdp_neg_was_answer_remote(inv->neg) &&
+ pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)==
+ PJSIP_DIALOG_CAP_SUPPORTED)
+ {
+ status = lock_codec(call);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to lock codec", status);
+ }
+ }
/* Call application callback, if any */
if (pjsua_var.ua_cfg.cb.on_call_media_state)
diff --git a/tests/pjsua/scripts-sipp/uas-answer-180-multiple-fmts-support-update.xml b/tests/pjsua/scripts-sipp/uas-answer-180-multiple-fmts-support-update.xml
new file mode 100644
index 00000000..e75e7c76
--- /dev/null
+++ b/tests/pjsua/scripts-sipp/uas-answer-180-multiple-fmts-support-update.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<!-- This program is free software; you can redistribute it and/or -->
+<!-- modify it under the terms of the GNU General Public License as -->
+<!-- published by the Free Software Foundation; either version 2 of the -->
+<!-- License, or (at your option) any later version. -->
+<!-- -->
+<!-- This program is distributed in the hope that it will be useful, -->
+<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
+<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
+<!-- GNU General Public License for more details. -->
+<!-- -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with this program; if not, write to the -->
+<!-- Free Software Foundation, Inc., -->
+<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
+<!-- -->
+<!-- Sipp default 'uas' scenario. -->
+<!-- -->
+
+<scenario name="UAS answer multiple formats in early media, UAS supports UPDATE method">
+ <!-- By adding rrs="true" (Record Route Sets), the route sets -->
+ <!-- are saved and used for following messages sent. Useful to test -->
+ <!-- against stateful SIP proxies/B2BUAs. -->
+ <recv request="INVITE" crlf="true">
+ <action>
+ <ereg regexp=".*" search_in="hdr" header="From" assign_to="3"/>
+ <ereg regexp="sip:(.*)>" search_in="hdr" header="Contact" assign_to="4,5"/>
+ <assign assign_to="4" variable="5" />
+ <ereg regexp=".*" search_in="hdr" header="Via" assign_to="6"/>
+ <ereg regexp=".*" search_in="hdr" header="CSeq" assign_to="7"/>
+ </action>
+ </recv>
+
+ <!-- The '[last_*]' keyword is replaced automatically by the -->
+ <!-- specified header if it was present in the last message received -->
+ <!-- (except if it was a retransmission). If the header was not -->
+ <!-- present or if no message has been received, the '[last_*]' -->
+ <!-- keyword is discarded, and all bytes until the end of the line -->
+ <!-- are also discarded. -->
+ <!-- -->
+ <!-- If the specified header was present several times in the -->
+ <!-- message, all occurences are concatenated (CRLF seperated) -->
+ <!-- to be used in place of the '[last_*]' keyword. -->
+
+ <send>
+ <![CDATA[
+
+ SIP/2.0 180 Ringing
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+ Allow: INVITE, UPDATE, ACK, BYE
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4004 RTP/AVP 0 8 3 111
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:8 PCMA/8000
+ a=rtpmap:3 GSM/8000
+ a=rtpmap:111 telephone-event/8000
+ a=fmtp:111 0-15
+
+ ]]>
+ </send>
+
+
+
+ <recv request="UPDATE" crlf="true">
+ </recv>
+
+ <send>
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+ Allow: INVITE, UPDATE, ACK, BYE
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4004 RTP/AVP 0 111
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:111 telephone-event/8000
+ a=fmtp:111 0-15
+
+ ]]>
+ </send>
+
+ <pause milliseconds="2000"/>
+
+ <send retrans="500">
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ Via[$6]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ CSeq[$7]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+ Allow: INVITE, UPDATE, ACK, BYE
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4004 RTP/AVP 0 111
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:111 telephone-event/8000
+ a=fmtp:111 0-15
+
+ ]]>
+ </send>
+
+ <recv request="ACK" crlf="true">
+ </recv>
+
+ <pause milliseconds="2000"/>
+
+ <send retrans="500">
+ <![CDATA[
+
+ BYE sip:[$5] SIP/2.0
+ Via: SIP/2.0/[transport] [local_ip]:[local_port]
+ From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+ To[$3]
+ Call-ID: [call_id]
+ Cseq: 1 BYE
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Max-Forwards: 70
+ Content-Length: 0
+
+ ]]>
+ </send>
+
+ <!-- Keep the call open for a while in case the 200 is lost to be -->
+ <!-- able to retransmit it if we receive the BYE again. -->
+ <pause milliseconds="4000"/>
+
+
+ <!-- definition of the response time repartition table (unit is ms) -->
+ <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
+
+ <!-- definition of the call length repartition table (unit is ms) -->
+ <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
+
+</scenario>
+
diff --git a/tests/pjsua/scripts-sipp/uas-answer-180-multiple-fmts.xml b/tests/pjsua/scripts-sipp/uas-answer-180-multiple-fmts.xml
new file mode 100644
index 00000000..bc27c9df
--- /dev/null
+++ b/tests/pjsua/scripts-sipp/uas-answer-180-multiple-fmts.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<!-- This program is free software; you can redistribute it and/or -->
+<!-- modify it under the terms of the GNU General Public License as -->
+<!-- published by the Free Software Foundation; either version 2 of the -->
+<!-- License, or (at your option) any later version. -->
+<!-- -->
+<!-- This program is distributed in the hope that it will be useful, -->
+<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
+<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
+<!-- GNU General Public License for more details. -->
+<!-- -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with this program; if not, write to the -->
+<!-- Free Software Foundation, Inc., -->
+<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
+<!-- -->
+<!-- Sipp default 'uas' scenario. -->
+<!-- -->
+
+<scenario name="UAS answer with multiple formats in early media">
+ <!-- By adding rrs="true" (Record Route Sets), the route sets -->
+ <!-- are saved and used for following messages sent. Useful to test -->
+ <!-- against stateful SIP proxies/B2BUAs. -->
+ <recv request="INVITE" crlf="true">
+ </recv>
+
+ <!-- The '[last_*]' keyword is replaced automatically by the -->
+ <!-- specified header if it was present in the last message received -->
+ <!-- (except if it was a retransmission). If the header was not -->
+ <!-- present or if no message has been received, the '[last_*]' -->
+ <!-- keyword is discarded, and all bytes until the end of the line -->
+ <!-- are also discarded. -->
+ <!-- -->
+ <!-- If the specified header was present several times in the -->
+ <!-- message, all occurences are concatenated (CRLF seperated) -->
+ <!-- to be used in place of the '[last_*]' keyword. -->
+
+ <send>
+ <![CDATA[
+
+ SIP/2.0 180 Ringing
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4004 RTP/AVP 0 8 3 111
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:8 PCMA/8000
+ a=rtpmap:3 GSM/8000
+ a=rtpmap:111 telephone-event/8000
+ a=fmtp:111 0-15
+
+ ]]>
+ </send>
+
+ <pause milliseconds="2000"/>
+
+ <send retrans="500">
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4004 RTP/AVP 0 8 3 111
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:8 PCMA/8000
+ a=rtpmap:3 GSM/8000
+ a=rtpmap:111 telephone-event/8000
+ a=fmtp:111 0-15
+
+ ]]>
+ </send>
+
+ <recv request="ACK" crlf="true">
+ </recv>
+
+
+
+ <recv request="INVITE" crlf="true">
+ <action>
+ <ereg regexp=".*" search_in="hdr" header="From" assign_to="3"/>
+ <ereg regexp="sip:(.*)>" search_in="hdr" header="Contact" assign_to="4,5"/>
+ <assign assign_to="4" variable="5" />
+ </action>
+ </recv>
+
+ <send retrans="500">
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4004 RTP/AVP 0 111
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:111 telephone-event/8000
+ a=fmtp:111 0-15
+
+ ]]>
+ </send>
+
+ <recv request="ACK" crlf="true">
+ </recv>
+
+
+ <pause milliseconds="2000"/>
+
+
+ <send retrans="500">
+ <![CDATA[
+
+ BYE sip:[$5] SIP/2.0
+ Via: SIP/2.0/[transport] [local_ip]:[local_port]
+ From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+ To[$3]
+ Call-ID: [call_id]
+ Cseq: 1 BYE
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Max-Forwards: 70
+ Content-Length: 0
+
+ ]]>
+ </send>
+
+ <!-- Keep the call open for a while in case the 200 is lost to be -->
+ <!-- able to retransmit it if we receive the BYE again. -->
+ <pause milliseconds="4000"/>
+
+
+ <!-- definition of the response time repartition table (unit is ms) -->
+ <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
+
+ <!-- definition of the call length repartition table (unit is ms) -->
+ <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
+
+</scenario>
+
diff --git a/tests/pjsua/scripts-sipp/uas-answer-200-multiple-fmts-support-update.xml b/tests/pjsua/scripts-sipp/uas-answer-200-multiple-fmts-support-update.xml
new file mode 100644
index 00000000..5d576003
--- /dev/null
+++ b/tests/pjsua/scripts-sipp/uas-answer-200-multiple-fmts-support-update.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<!-- This program is free software; you can redistribute it and/or -->
+<!-- modify it under the terms of the GNU General Public License as -->
+<!-- published by the Free Software Foundation; either version 2 of the -->
+<!-- License, or (at your option) any later version. -->
+<!-- -->
+<!-- This program is distributed in the hope that it will be useful, -->
+<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
+<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
+<!-- GNU General Public License for more details. -->
+<!-- -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with this program; if not, write to the -->
+<!-- Free Software Foundation, Inc., -->
+<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
+<!-- -->
+<!-- Sipp default 'uas' scenario. -->
+<!-- -->
+
+<scenario name="UAS answer multiple formats, UAS supports UPDATE method">
+ <!-- By adding rrs="true" (Record Route Sets), the route sets -->
+ <!-- are saved and used for following messages sent. Useful to test -->
+ <!-- against stateful SIP proxies/B2BUAs. -->
+ <recv request="INVITE" crlf="true">
+ <action>
+ <ereg regexp=".*" search_in="hdr" header="From" assign_to="3"/>
+ <ereg regexp="sip:(.*)>" search_in="hdr" header="Contact" assign_to="4,5"/>
+ <assign assign_to="4" variable="5" />
+ </action>
+ </recv>
+
+ <!-- The '[last_*]' keyword is replaced automatically by the -->
+ <!-- specified header if it was present in the last message received -->
+ <!-- (except if it was a retransmission). If the header was not -->
+ <!-- present or if no message has been received, the '[last_*]' -->
+ <!-- keyword is discarded, and all bytes until the end of the line -->
+ <!-- are also discarded. -->
+ <!-- -->
+ <!-- If the specified header was present several times in the -->
+ <!-- message, all occurences are concatenated (CRLF seperated) -->
+ <!-- to be used in place of the '[last_*]' keyword. -->
+
+ <send retrans="500">
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+ Allow: INVITE, UPDATE, ACK, BYE
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4004 RTP/AVP 0 8 3 111
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:8 PCMA/8000
+ a=rtpmap:3 GSM/8000
+ a=rtpmap:111 telephone-event/8000
+ a=fmtp:111 0-15
+
+ ]]>
+ </send>
+
+ <recv request="ACK" crlf="true">
+ </recv>
+
+
+
+ <recv request="UPDATE" crlf="true">
+ </recv>
+
+ <send>
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+ Allow: INVITE, UPDATE, ACK, BYE
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4004 RTP/AVP 0 111
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:111 telephone-event/8000
+ a=fmtp:111 0-15
+
+ ]]>
+ </send>
+
+ <pause milliseconds="2000"/>
+
+ <send retrans="500">
+ <![CDATA[
+
+ BYE sip:[$5] SIP/2.0
+ Via: SIP/2.0/[transport] [local_ip]:[local_port]
+ From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+ To[$3]
+ Call-ID: [call_id]
+ Cseq: 1 BYE
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Max-Forwards: 70
+ Content-Length: 0
+
+ ]]>
+ </send>
+
+ <!-- Keep the call open for a while in case the 200 is lost to be -->
+ <!-- able to retransmit it if we receive the BYE again. -->
+ <pause milliseconds="4000"/>
+
+
+ <!-- definition of the response time repartition table (unit is ms) -->
+ <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
+
+ <!-- definition of the call length repartition table (unit is ms) -->
+ <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
+
+</scenario>
+
diff --git a/tests/pjsua/scripts-sipp/uas-answer-200-multiple-fmts.xml b/tests/pjsua/scripts-sipp/uas-answer-200-multiple-fmts.xml
new file mode 100644
index 00000000..4e4170d2
--- /dev/null
+++ b/tests/pjsua/scripts-sipp/uas-answer-200-multiple-fmts.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE scenario SYSTEM "sipp.dtd">
+
+<!-- This program is free software; you can redistribute it and/or -->
+<!-- modify it under the terms of the GNU General Public License as -->
+<!-- published by the Free Software Foundation; either version 2 of the -->
+<!-- License, or (at your option) any later version. -->
+<!-- -->
+<!-- This program is distributed in the hope that it will be useful, -->
+<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
+<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
+<!-- GNU General Public License for more details. -->
+<!-- -->
+<!-- You should have received a copy of the GNU General Public License -->
+<!-- along with this program; if not, write to the -->
+<!-- Free Software Foundation, Inc., -->
+<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
+<!-- -->
+<!-- Sipp default 'uas' scenario. -->
+<!-- -->
+
+<scenario name="UAS answer multiple formats">
+ <!-- By adding rrs="true" (Record Route Sets), the route sets -->
+ <!-- are saved and used for following messages sent. Useful to test -->
+ <!-- against stateful SIP proxies/B2BUAs. -->
+ <recv request="INVITE" crlf="true">
+ </recv>
+
+ <!-- The '[last_*]' keyword is replaced automatically by the -->
+ <!-- specified header if it was present in the last message received -->
+ <!-- (except if it was a retransmission). If the header was not -->
+ <!-- present or if no message has been received, the '[last_*]' -->
+ <!-- keyword is discarded, and all bytes until the end of the line -->
+ <!-- are also discarded. -->
+ <!-- -->
+ <!-- If the specified header was present several times in the -->
+ <!-- message, all occurences are concatenated (CRLF seperated) -->
+ <!-- to be used in place of the '[last_*]' keyword. -->
+
+ <send retrans="500">
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4004 RTP/AVP 0 8 3 111
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:8 PCMA/8000
+ a=rtpmap:3 GSM/8000
+ a=rtpmap:111 telephone-event/8000
+ a=fmtp:111 0-15
+
+ ]]>
+ </send>
+
+ <recv request="ACK" crlf="true">
+ </recv>
+
+
+
+ <recv request="INVITE" crlf="true">
+ <action>
+ <ereg regexp=".*" search_in="hdr" header="From" assign_to="3"/>
+ <ereg regexp="sip:(.*)>" search_in="hdr" header="Contact" assign_to="4,5"/>
+ <assign assign_to="4" variable="5" />
+ </action>
+ </recv>
+
+ <send retrans="500">
+ <![CDATA[
+
+ SIP/2.0 200 OK
+ [last_Via:]
+ [last_From:]
+ [last_To:];tag=[call_number]
+ [last_Call-ID:]
+ [last_CSeq:]
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Content-Type: application/sdp
+ Content-Length: [len]
+
+ v=0
+ o=- 3441953879 3441953879 IN IP4 192.168.0.15
+ s=pjmedia
+ c=IN IP4 192.168.0.15
+ t=0 0
+ m=audio 4004 RTP/AVP 0 111
+ a=rtpmap:0 PCMU/8000
+ a=rtpmap:111 telephone-event/8000
+ a=fmtp:111 0-15
+
+ ]]>
+ </send>
+
+ <recv request="ACK" crlf="true">
+ </recv>
+
+ <pause milliseconds="2000"/>
+
+ <send retrans="500">
+ <![CDATA[
+
+ BYE sip:[$5] SIP/2.0
+ Via: SIP/2.0/[transport] [local_ip]:[local_port]
+ From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]
+ To[$3]
+ Call-ID: [call_id]
+ Cseq: 1 BYE
+ Contact: sip:sipp@[local_ip]:[local_port]
+ Max-Forwards: 70
+ Content-Length: 0
+
+ ]]>
+ </send>
+
+ <!-- Keep the call open for a while in case the 200 is lost to be -->
+ <!-- able to retransmit it if we receive the BYE again. -->
+ <pause milliseconds="4000"/>
+
+
+ <!-- definition of the response time repartition table (unit is ms) -->
+ <ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
+
+ <!-- definition of the call length repartition table (unit is ms) -->
+ <CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
+
+</scenario>
+