Hi,
I've run into an issue with handling reinvites in async task workers--which I need to do in order to introduce a small delay to avoid a UA race condition--in which some state that is necessary for rtpengine_manage() to do the right thing seems to be lost.
Specifically, if I do this:
-- loadmodule "mqueue" loadmodule "rtimer"
...
modparam("rtimer", "timer", "name=reinvite_q1;interval=10000u;mode=1;") modparam("rtimer", "exec", "timer=reinvite_q1;route=REINVITE_DEQUEUE")
modparam("mqueue", "mqueue", "name=reinvite_q");
...
route { ...
if(has_totag()) { if(loose_route()) { ...
if(is_method("INVITE")) { if(!t_suspend()) { sl_send_reply("500", "Server Internal Error"); exit; }
mq_add("reinvite_q", "$T(id_index):$T(id_label)", ""); } else route(IN_DLG_REQ); exit; }
... }
route[REINVITE_DEQUEUE] { while(mq_fetch("reinvite_q")) { $var(id) = $(mqk(reinvite_q){s.select,0,:}{s.int}); $var(label) = $(mqk(reinvite_q){s.select,1,:}{s.int});
t_continue("$var(id)", "$var(label)", "IN_DLG_REQ"); } }
route[IN_DLG_REQ] { handle_ruri_alias();
if(is_method("BYE")) rtpengine_manage(); else if(is_method("INVITE") && sdp_content()) rtpengine_manage("replace-origin replace-session-connection ICE=remove")
t_on_reply("MAIN_REPLY");
if(!t_relay()) sl_reply_error(); }
onreply_route[MAIN_REPLY] { if(is_method("INVITE") && sdp_content()) rtpengine_manage("replace-origin replace-session-connection ICE=remove"); } --
What I find is that if I generate a reinvite from either side, rtpengine_manage() passes through the SDP offer in the reinvite unadulterated, although the answer returns properly adulterated by RTPEngine.
The same thing occurs if I use 'async', which as I gather is just a wrapper of convenience around t_suspend/t_continue() anyway. That module won't do for my purposes because I need a millisecond-level delay resolution rather than second-level, anyhow.
Anyway, surely enough, this is because the execution context is one that insinuates a stream deletion. When the reinvite offer comes across and I call rtpengine_manage(), the command sent is 'delete':
-- Jan 14 20:24:23 staccato.evaristesys.com rtpengine[33830]: INFO: [c94347dcf92e7ccc10dee84c4e0d1c8a]: Received command 'delete' from 209.51.167.66:39788 --
The RTPEngine module documentation actually warns about this:
https://kamailio.org/docs/modules/5.2.x/modules/rtpengine.html#rtpengine.f.r...
But this insight is buried in the fourth bullet point:
"If BYE or CANCEL, or called within a FAILURE_ROUTE[], then call rtpengine_delete(). Be careful with calling this function after resuming a suspended transaction (e.g., after t_continue()), because the context of executed route is FAILURE ROUTE (in other words, rtpengine_manage() in the route block of t_continue() does the same as in failure_route)."
This would lead one to believe that it applies only in the context of handling a BYE or CANCEL in a resumed transaction, when in fact it seems to hold true of non-BYE/CANCEL and non-failure contexts as well, as can be clearly seen with the reinvite.
The behaviour is exactly as expected if I don't use rtpengine_manage() and instead manually call rtpengine_offer()/rtpengine_answer(). And expected behaviour comes from rtpengine_manage() if I don't handle the request in a resumed transaction context.
This yields the fundamental question:
Is there any way to have my cake and eat it too? I really want to use rtpengine_manage() because I need to handle a potential SDP offer-in-ACK scenario and don't want to do a bunch of state-keeping in order to use the correct choice of rtpengine_{offer|answer}() in the right context, which is a problem rtpengine_manage() solves beautifully. At the same time, I absolutely need the slight reinvite delay that can be accomplished via async processing; a UA on this side has a nasty habit of sending a reinvite almost simultaneously to the 2xx answer for the INVITE transaction, leading to indeterminate egress velocity for those respective messages out of Kamailio worker threads.
Many thanks in advance!
-- Alex