Commit b5f7f4e3 authored by Pekka Pessi's avatar Pekka Pessi

nua: optionally include answer in 1XX response.

The SDP answer is included in non-realiable 1XX response, if
NUTAG_EARLY_ANSWER(1) is included in nua_response() tags, or the user SDP is
specified with SOATAG_USER_SDP() or SOATAG_USER_SDP_STR() in nua_response()
tags.

test_basic_call.c: added test_basic_call_2() for testing the call setup
where 180 contains the answer and the SDP in 200 OK is ignored.

darcs-hash:20060907141236-65a35-e0364e67f4c5d446bf7195e1c5dc721ddaaa0894.gz
parent 5e547ba2
......@@ -575,23 +575,19 @@ void nua_unregister(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
/** Place a call using SIP INVITE method.
*
* By default creates a media session, includes its description as
* SDP and send the request to the recipient. Upon receiving the
* response it will active the media session and establish the call.
* By default creates a SOA session, includes its description as SDP and
* sends the request to the recipient. Upon receiving the response nua will
* activate the media session and establish the call.
*
* Incomplete call can be hung-up with nua_cancel(). Completed call can be
* hung-up with nua_bye().
* Incomplete call can be hung-up with nua_cancel(). Complete or incomplete
* calls can be hung-up with nua_bye().
*
* Optionally
* - uses early media if NUTAG_EARLY_MEDIA() tag is used with non zero value
* - media parameters can be set by NUTAG_MEDIA_* tags
* - if NUTAG_MEDIA_ENABLE() tag is used with value zero then the soa is
* not used and application must create the SDP
* - nua_invite() can be used to change call status:
* - media parameters can be set by SOA tags
* - nua_invite() can be used to change status of an existing call:
* - #SOATAG_HOLD tag listing the media put on hold or with value "*" sets
* the call on hold
* - if new media path is given either new media parameters are taken in
* use or new media is added to session.
*
* @param nh Pointer to operation handle
* @param tag, value, ... List of tagged parameters
......@@ -606,7 +602,6 @@ void nua_unregister(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
* NUTAG_REFER_PAUSE() \n
* NUTAG_INVITE_TIMER() \n
* NUTAG_MEDIA_FEATURES() \n
* NUTAG_MEDIA_ENABLE() \n
* SOATAG_HOLD() \n
* SOATAG_AF() \n
* SOATAG_ADDRESS() \n
......@@ -618,11 +613,13 @@ void nua_unregister(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...)
* #nua_i_state \n
* #nua_i_active \n
* #nua_i_media_error \n
* #nua_i_terminated \n
* #nua_i_fork \n
*
* \sa nua_handle_has_active_call() \n
* nua_handle_has_call_on_hold()\n
* nua_handle_has_invite() \n
* nua_prack() \n
* nua_update() \n
* nua_info() \n
* nua_cancel() \n
......
......@@ -218,6 +218,7 @@ int nua_stack_init_instance(nua_handle_t *nh, tagi_t const *tags)
* NUTAG_KEEPALIVE() \n
* NUTAG_KEEPALIVE_STREAM() \n
* NUTAG_MAX_SUBSCRIPTIONS() \n
* NUTAG_MEDIA_ENABLE() \n
* NUTAG_MEDIA_FEATURES() \n
* NUTAG_MIN_SE() \n
* NUTAG_OUTBOUND() \n
......@@ -309,7 +310,6 @@ int nua_stack_init_instance(nua_handle_t *nh, tagi_t const *tags)
* none
*/
int nua_stack_set_params(nua_t *nua, nua_handle_t *nh, nua_event_t e,
tagi_t const *tags)
{
......
......@@ -911,6 +911,9 @@ nua_stack_cancel(nua_t *nua, nua_handle_t *nh, nua_event_t e,
nua_client_request_t *cri = ss->ss_crequest;
nua_client_request_t *crc = nh->nh_cr;
if (tags)
nua_stack_set_params(nua, nh, nua_r_cancel, tags);
if (nh && cri->cr_orq && cri->cr_usage) {
nta_outgoing_t *orq;
......@@ -1191,7 +1194,7 @@ void respond_to_invite(nua_t *nua, nua_handle_t *nh,
nua_session_state_t *ss = nh->nh_ss;
nua_server_request_t *sr = ss->ss_srequest;
int autoanswer = 0, offer = 0, answer = 0;
int autoanswer = 0, offer = 0, answer = 0, early_answer = 0;
enter;
......@@ -1207,8 +1210,24 @@ void respond_to_invite(nua_t *nua, nua_handle_t *nh,
assert(ss->ss_usage);
if (nh->nh_soa)
soa_set_params(nh->nh_soa, TAG_NEXT(tags));
if (tags) {
nua_stack_set_params(nua, nh, nua_i_invite, tags);
if (status < 200) {
early_answer = -1;
tl_gets(tags, NUTAG_EARLY_ANSWER_REF(early_answer), TAG_END());
if (early_answer == -1) {
sdp_session_t const *user_sdp = NULL;
char const *user_sdp_str = NULL;
tl_gets(tags,
SOATAG_USER_SDP_REF(user_sdp),
SOATAG_USER_SDP_STR_REF(user_sdp_str),
TAG_END());
early_answer = user_sdp || user_sdp_str;
}
}
}
msg = nh_make_response(nua, nh, ss->ss_srequest->sr_irq,
status, phrase,
......@@ -1248,8 +1267,9 @@ void respond_to_invite(nua_t *nua, nua_handle_t *nh,
else if (status >= 300) {
soa_clear_remote_sdp(nh->nh_soa);
}
else if (status >= 200 || ss->ss_100rel) {
if ((sr->sr_offer_recv && sr->sr_answer_sent) ||
else if (status >= 200 || ss->ss_100rel ||
(sr->sr_offer_recv && !sr->sr_answer_sent && early_answer)) {
if ((sr->sr_offer_recv && sr->sr_answer_sent > 1) ||
(sr->sr_offer_sent && !sr->sr_answer_recv))
/* Nothing to do */;
else if (sr->sr_offer_recv && !sr->sr_answer_sent) {
......@@ -1273,6 +1293,8 @@ void respond_to_invite(nua_t *nua, nua_handle_t *nh,
/* signal that O/A answer sent (answer to invite) */
}
}
else if (sr->sr_offer_recv && sr->sr_answer_sent == 1)
answer = 1;
else if (!sr->sr_offer_recv && !sr->sr_offer_sent) {
if (soa_generate_offer(nh->nh_soa, 0, NULL) < 0)
status = soa_error_as_sip_response(nh->nh_soa, &phrase);
......
......@@ -109,7 +109,9 @@ struct nua_client_request
nua_dialog_usage_t *cr_usage;
unsigned short cr_retry_count; /**< Retry count for this request */
unsigned short cr_answer_recv; /**< Recv answer in response */
unsigned short cr_answer_recv; /**< Recv answer in response
* with this status.
*/
unsigned cr_offer_sent:1; /**< Sent offer in this request */
unsigned cr_offer_recv:1; /**< Recv offer in a response */
......
......@@ -51,6 +51,7 @@ tag_typedef_t nutag_media_features = BOOLTAG_TYPEDEF(media_features);
tag_typedef_t nutag_callee_caps = BOOLTAG_TYPEDEF(callee_caps);
tag_typedef_t nutag_early_media = BOOLTAG_TYPEDEF(early_media);
tag_typedef_t nutag_only183_100rel = BOOLTAG_TYPEDEF(only183_100rel);
tag_typedef_t nutag_early_answer = BOOLTAG_TYPEDEF(early_answer);
tag_typedef_t nutag_media_enable = BOOLTAG_TYPEDEF(media_enable);
tag_typedef_t nutag_soa_session = PTRTAG_TYPEDEF(soa_session);
......
......@@ -237,21 +237,27 @@ SOFIAPUBVAR tag_typedef_t nutag_soa_name;
nutag_soa_name_ref, tag_str_vr(&(x))
SOFIAPUBVAR tag_typedef_t nutag_soa_name_ref;
/**Establish early media session using 183 responses and PRACK requests.
/**Establish early media session using 100rel, 183 responses and PRACK.
*
* @par Used with
* nua_set_params() \n
* nua_get_params()
* nua_get_params() \n
* nua_set_hparams() \n
* nua_get_hparams() \n
* nua_invite() \n
* nua_respond() \n
*
* @par Parameter type
* int
* int (boolean)
*
* @par Values
* @c 0 False \n
* @c !=0 True
*
* @sa NUTAG_EARLY_ANWER()
*
* Corresponding tag taking reference parameter is NUTAG_EARLY_MEDIA_REF()
*/
*/
#define NUTAG_EARLY_MEDIA(x) nutag_early_media, tag_bool_v(x)
SOFIAPUBVAR tag_typedef_t nutag_early_media;
......@@ -265,7 +271,11 @@ SOFIAPUBVAR tag_typedef_t nutag_early_media_ref;
*
* @par Used with
* nua_set_params() \n
* nua_get_params()
* nua_get_params() \n
* nua_set_hparams() \n
* nua_get_hparams() \n
* nua_invite() \n
* nua_respond()
*
* @par Parameter type
* int (boolean)
......@@ -282,6 +292,28 @@ SOFIAPUBVAR tag_typedef_t nutag_only183_100rel;
#define NUTAG_ONLY183_100REL_REF(x) nutag_only183_100rel_ref, tag_bool_vr(&(x))
SOFIAPUBVAR tag_typedef_t nutag_only183_100rel_ref;
/**Establish early media session by including SDP answer in 1XX response.
*
* @par Used with
* nua_respond() \n
*
* @par Parameter type
* int (boolean)
*
* @par Values
* @c 0 False \n
* @c !=0 True
*
* @sa NUTAG_EARLY_MEDIA_REF()
*
* Corresponding tag taking reference parameter is NUTAG_EARLY_ANSWER_REF()
*/
#define NUTAG_EARLY_ANSWER(x) nutag_early_answer, tag_bool_v(x)
SOFIAPUBVAR tag_typedef_t nutag_early_answer;
#define NUTAG_EARLY_ANSWER_REF(x) nutag_early_answer_ref, tag_bool_vr(&(x))
SOFIAPUBVAR tag_typedef_t nutag_early_answer_ref;
/** Timer for outstanding INVITE in seconds.
*
* INVITE will be canceled if no answer is received before timer expires.
......
......@@ -149,7 +149,7 @@ int until_ready(CONDITION_PARAMS)
See @page nua_call_model in nua.docs for more information
*/
int test_basic_call(struct context *ctx)
int test_basic_call_1(struct context *ctx)
{
BEGIN();
......@@ -259,3 +259,169 @@ int test_basic_call(struct context *ctx)
END();
}
/*
accept_early_answer
X ep
| |
|-------INVITE------>|
|<----100 Trying-----|
| |
|<----180 Ringing----|
| |
|<--------200--------|
|---------ACK------->|
*/
int accept_early_answer(CONDITION_PARAMS)
{
if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR)))
return 0;
save_event_in_list(ctx, event, ep, call);
switch (callstate(tags)) {
case nua_callstate_received:
RESPOND(ep, call, nh, SIP_180_RINGING,
NUTAG_EARLY_ANSWER(1),
TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
TAG_END());
return 0;
case nua_callstate_early:
RESPOND(ep, call, nh, SIP_200_OK,
TAG_IF(call->sdp, SOATAG_USER_SDP_STR(call->sdp)),
TAG_END());
return 0;
case nua_callstate_ready:
return 1;
case nua_callstate_terminated:
if (call)
nua_handle_destroy(call->nh), call->nh = NULL;
return 1;
default:
return 0;
}
}
int test_basic_call_2(struct context *ctx)
{
BEGIN();
struct endpoint *a = &ctx->a, *b = &ctx->b;
struct call *a_call = a->call, *b_call = b->call;
struct event *e;
sip_t const *sip;
if (print_headings)
printf("TEST NUA-3.2: Basic call with SDP in 180\n");
a_call->sdp = "m=audio 5008 RTP/AVP 8";
b_call->sdp = "m=audio 5010 RTP/AVP 0 8";
TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END()));
TEST_1(!nua_handle_has_active_call(a_call->nh));
TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
INVITE(a, a_call, a_call->nh,
TAG_IF(!ctx->proxy_tests, NUTAG_URL(b->contact->m_url)),
SOATAG_USER_SDP_STR(a_call->sdp),
TAG_END());
run_ab_until(ctx, -1, until_ready, -1, accept_early_answer);
/* Client transitions:
INIT -(C1)-> CALLING: nua_invite(), nua_i_state
CALLING -(C2)-> PROCEEDING: nua_r_invite, nua_i_state
PROCEEDING -(C3+C4)-> READY: nua_r_invite, nua_i_state
*/
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_calling); /* CALLING */
TEST_1(is_offer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 180);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_proceeding); /* PROCEEDING */
TEST_1(is_answer_recv(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_invite);
TEST(e->data->e_status, 200);
TEST_1(sip = sip_object(e->data->e_msg));
TEST_1(sip->sip_content_type);
TEST_S(sip->sip_content_type->c_type, "application/sdp");
TEST_1(sip->sip_payload); /* there is sdp in 200 OK */
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
TEST_1(!is_answer_recv(e->data->e_tags)); /* but it is ignored */
TEST_1(!e->next);
free_events_in_list(ctx, a->events);
TEST_1(nua_handle_has_active_call(a_call->nh));
TEST_1(!nua_handle_has_call_on_hold(a_call->nh));
/*
Server transitions:
INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state
RECEIVED -(S2a)-> EARLY: nua_respond(), nua_i_state
EARLY -(S3b)-> COMPLETED: nua_respond(), nua_i_state
COMPLETED -(S4)-> READY: nua_i_ack, nua_i_state
*/
TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite);
TEST(e->data->e_status, 100);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_received); /* RECEIVED */
TEST_1(is_offer_recv(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_early); /* EARLY */
TEST_1(is_answer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_completed); /* COMPLETED */
TEST_1(is_answer_sent(e->data->e_tags));
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_ack);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_ready); /* READY */
TEST_1(!e->next);
free_events_in_list(ctx, b->events);
TEST_1(nua_handle_has_active_call(b_call->nh));
TEST_1(!nua_handle_has_call_on_hold(b_call->nh));
BYE(b, b_call, b_call->nh, TAG_END());
run_ab_until(ctx, -1, until_terminated, -1, until_terminated);
/* B transitions:
READY --(T2)--> TERMINATING: nua_bye()
TERMINATING --(T3)--> TERMINATED: nua_r_bye, nua_i_state
*/
TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_r_bye);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
TEST_1(!e->next);
free_events_in_list(ctx, b->events);
TEST_1(!nua_handle_has_active_call(b_call->nh));
/* A transitions:
READY -(T1)-> TERMINATED: nua_i_bye, nua_i_state
*/
TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_i_bye);
TEST(e->data->e_status, 200);
TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state);
TEST(callstate(e->data->e_tags), nua_callstate_terminated); /* TERMINATED */
TEST_1(!e->next);
free_events_in_list(ctx, a->events);
TEST_1(!nua_handle_has_active_call(a_call->nh));
nua_handle_destroy(a_call->nh), a_call->nh = NULL;
nua_handle_destroy(b_call->nh), b_call->nh = NULL;
if (print_headings)
printf("TEST NUA-3.2: PASSED\n");
END();
}
int test_basic_call(struct context *ctx)
{
return test_basic_call_1(ctx) || test_basic_call_2(ctx);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment