MT#60408 Answer with 'inactive' when 'inactive' requested during the on hold

This commit affects cases when the on-hold is requested,
and the MoH emulation on the SEMS is additionally used.

Currently regardless the method used by the one who sets the call
on-hold, the answer will be always 'a=sendrecv', which leads to
a premature termination of the call at the RTPEngine
(because RTPEngine takes the 'a=sendrecv' into account and
terminates the call due to no seeing the audio)

We need to make sure, that an originator of the on-hold, is being
answered:
- with the 'a=recvonly', if an originator requests the 'a=sendonly'
  method of the on-hold to be used.
- with the 'a=inactive', if an originator requests the 'a=inactive'
  method of the on-hold to be used.

And after the on-hold is restored (by 'a=sendrecv'),
make sure to send the SDP answer with the 'a=sendrecv' as well.

Change-Id: I50df81be16a75d9e813484012938e2a94ec422e1
mr12.5.1
Donat Zenichev 2 years ago
parent b21a430983
commit c0452f5b2c

@ -240,7 +240,8 @@ CallLeg::CallLeg(const CallLeg* caller, AmSipDialog* p_dlg, AmSipSubscription* p
: AmB2BSession(caller->getLocalTag(),p_dlg,p_subs),
call_status(Disconnected),
on_hold(false),
hold(PreserveHoldStatus)
hold(PreserveHoldStatus),
hold_method_requested(NonHold)
{
a_leg = !caller->a_leg; // we have to be the complement
@ -300,7 +301,8 @@ CallLeg::CallLeg(AmSipDialog* p_dlg, AmSipSubscription* p_subs)
: AmB2BSession("",p_dlg,p_subs),
call_status(Disconnected),
on_hold(false),
hold(PreserveHoldStatus)
hold(PreserveHoldStatus),
hold_method_requested(NonHold)
{
a_leg = true;
@ -1126,7 +1128,22 @@ void CallLeg::onSipRequest(const AmSipRequest& req)
* to avoid other confusions... */
dlg->reply(req, 200, "OK");
} else {
}
else
{
/** only for requests which put the call on hold.
* Remember that we have to answer to the one, who puts on hold,
* with the 'inactive' back (as soon as the on hold is accepted with the 200OK
* by the other side of the call) in case the on hold was requested using 'inactive'
*/
AmSdp sdp;
hm hold_method;
if (req.method == SIP_METH_INVITE && retrieveAmSdp(req.body, sdp)) {
/* case when remote side puts us on hold */
if (isOnHoldRequested(sdp, hold_method)) updateHoldMethod(hold_method);
/* it's likely sendrecv - then make sure 'hold_method_requested' is kept updated */
else hold_method_requested = NonHold;
}
AmB2BSession::onSipRequest(req);
}
}
@ -1397,6 +1414,41 @@ void CallLeg::updateCallStatus(CallStatus new_status, const StatusChangeCause &c
onCallStatusChange(cause);
}
void CallLeg::updateHoldMethod(const hm &hm)
{
switch (hm)
{
case SendonlyStream: hold_method_requested = SendonlyHold; break;
case InactiveStream: hold_method_requested = InactiveHold; break;
case ZeroedConnection: hold_method_requested = ZeroedHold; break;
default: hold_method_requested = NonHold;
}
DBG("hold_method_requested is set to: <%d> for LT <%s>\n",
hold_method_requested, getLocalTag().c_str());
}
bool CallLeg::isOnHoldRequested(const AmSdp &sdp, hm &method)
{
if (isHoldRequest(sdp, (HoldMethod&)method)) {
DBG("This request puts the call on hold\n");
return true;
}
return false;
}
bool CallLeg::retrieveAmSdp(const AmMimeBody &mSdp, AmSdp &sdp)
{
AmMimeBody t_sdp_body = mSdp;
AmMimeBody * sdp_body = t_sdp_body.hasContentType(SIP_APPLICATION_SDP);
if (!sdp_body) {
DBG("Failed to parse SDP body while retrieving into AmSdp!\n");
return false;
}
if (sdp.parse((const char *)sdp_body->getPayload())) {
DBG("Failed to parse SDP body while retrieving into AmSdp!\n");
return false;
}
return true; /* parsed successfully */
}
void CallLeg::addExistingCallee(const string &session_tag, ReconnectLegEvent *ev)
{
// add existing session as our B leg
@ -1794,11 +1846,40 @@ void CallLeg::updateLocalSdp(AmSdp &sdp)
adjustOffer(sdp);
}
if (hold == PreserveHoldStatus && !on_hold) {
// store non-hold SDP to be able to resumeHeld
non_hold_sdp = sdp;
/** make sure to answer with the 'sendonly' / 'inactive' to the one who previously
* requested the on-hold, in order to meet RFC requirements:
* - 'sendonly' must be faced with the 'recvonly' sent back as an answer
* - 'inactive' must be faced with the 'inactive' sent back as an answer
* This block is only needed for cases, when MoH is emulated on the SEMS directly.
*/
else if (hold == PreserveHoldStatus && hold_method_requested != NonHold)
{
for (std::vector<SdpMedia>::iterator m = sdp.media.begin();
m != sdp.media.end(); ++m)
{
if (m->isAudio()) {
switch(hold_method_requested)
{
case SendonlyHold:
m->send = false; /* make sure to answer with the recvonly, if the on hold */
m->recv = true; /* was perviously requested using a=sendonly */
DBG("On hold has been previously requested and must be held further\n");
DBG("This SDP is prepared to be sent with the a=recvonly\n");
break;
case InactiveHold:
m->send = false; /* make sure to answer with the inactive, if the on hold */
m->recv = false; /* was perviously requested using a=inactive */
DBG("On hold has been previously requested and must be held further\n");
DBG("This SDP is prepared to be sent with the a=inactive\n");
break;
}
}
}
}
/* store non-hold SDP to be able to resumeHeld */
if (hold == PreserveHoldStatus && !on_hold) non_hold_sdp = sdp;
AmB2BSession::updateLocalSdp(sdp);
}

@ -142,6 +142,7 @@ class CallLeg: public AmB2BSession
bool on_hold; // remote is on hold
AmSdp non_hold_sdp;
enum { HoldRequested, ResumeRequested, PreserveHoldStatus } hold;
enum { NonHold, InactiveHold, SendonlyHold, ZeroedHold } hold_method_requested;
// queue of session update operations, first element is possibly the one
// being in progress
@ -189,6 +190,24 @@ class CallLeg: public AmB2BSession
void updateCallStatus(CallStatus new_status, const StatusChangeCause &cause = StatusChangeCause());
/** TODO: bring 'enum HoldMethod' from the .cpp file inside the CallLeg class definition.
* Currently this is a rude implementation which creates obstacles,
* and forces us to have a duplicate (hm) here in the class, when we want to have it as param.
* Additionally we have to constantly cast HoldMethod -> hm while working with them.
*/
enum hm { SendonlyStream, InactiveStream, ZeroedConnection };
/** keep the method, which was used to put the call
* on hold, in the SDP offer received (most likely in re-INVITE)
*/
void updateHoldMethod(const hm &hm);
/* check if this request assumes call to be put on hold,
* this in internal class'es implementation.
* hm - hold method (SendonlyStream, InactiveStream, ZeroedConnection)
*/
bool isOnHoldRequested(const AmSdp &sdp, hm &hm);
/* set AmSdp based on AmMimeBody */
bool retrieveAmSdp(const AmMimeBody &mSdp, AmSdp &sdp);
//////////////////////////////////////////////////////////////////////
// callbacks (intended to be redefined in successors but should not be
// called by them directly)

Loading…
Cancel
Save