Skip to content

Commit

Permalink
media_exchange: add commands for pausing/resuming forks
Browse files Browse the repository at this point in the history
  • Loading branch information
razvancrainea committed Mar 6, 2020
1 parent 3e69d2a commit 2686630
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 16 deletions.
81 changes: 81 additions & 0 deletions modules/media_exchange/doc/media_exchange_admin.xml
Expand Up @@ -235,6 +235,87 @@ if (!has_totag() && is_method("INVITE") && $hdr(X-CallID) != NUL
...
if (!has_totag() && is_method("INVITE") && $hdr(X-CallID) != NULL)
media_fork_from_call($hdr(X-CallID), "caller", 0);
...
</programlisting>
</example>
</section>
<section id="func_media_fork_pause" xreflabel="media_fork_pause()">
<title>
<function moreinfo="none">media_fork_pause([leg[, medianum]])</function>
</title>
<para>
Pauses an existing RTP media streaming session. This function does
not terminate the forking call, but only stops sending the RTP.
It also re-invites the Media Server to inform about the change.
</para>
<para>
Parameters:
<itemizedlist>
<listitem><para>
<emphasis>leg</emphasis> (string, optional) - the leg that will
be paused. Possible values are <emphasis>caller</emphasis>,
<emphasis>callee</emphasis> and <emphasis>both</emphasis>. If
missing, all ongoing media sessions will be paused.
</para></listitem>
</itemizedlist>
<listitem><para>
<emphasis>medianum</emphasis> (integer, optional) - the media
stream to be paused. First index is 0.
If missing, all ongoing media streams associated to the
selected leg will be paused.
</para></listitem>
</para>
<para>
This function can be used from REQUEST_ROUTE, BRANCH_ROUTE,
ONREPLY_ROUTE, EVENT_ROUTE.
</para>
<example>
<title>Use <function>media_fork_pause()</function> function to temporarily
stop the entire media stream of the call</title>
<programlisting format="linespecific">
...
if (has_totag() &amp;&amp; is_method("INVITE"))
media_fork_pause();
...
</programlisting>
</example>
</section>
<section id="func_media_fork_resume" xreflabel="media_fork_resume()">
<title>
<function moreinfo="none">media_fork_resume([leg[, medianum]])</function>
</title>
<para>
Resumes the RTP media stream of an existing session/call. This function
relies on the fact that a media fork session has been previously started.
</para>
<para>
Parameters:
<itemizedlist>
<listitem><para>
<emphasis>leg</emphasis> (string, optional) - the leg that will
be resumed. Possible values are <emphasis>caller</emphasis>,
<emphasis>callee</emphasis> and <emphasis>both</emphasis>. If
missing, all existing media legs that are stopped will be started.
</para></listitem>
</itemizedlist>
<listitem><para>
<emphasis>medianum</emphasis> (integer, optional) - the media
stream to be paused. First index is 0.
If missing, all ongoing media streams associated to the
selected leg will be paused.
</para></listitem>
</para>
<para>
This function can be used from REQUEST_ROUTE, BRANCH_ROUTE,
ONREPLY_ROUTE, EVENT_ROUTE.
</para>
<example>
<title>Use <function>media_fork_resume()</function> function to resume
a forking previously stopped</title>
<programlisting format="linespecific">
...
if (has_totag() &amp;&amp; is_method("INVITE"))
media_fork_resume();
...
</programlisting>
</example>
Expand Down
118 changes: 118 additions & 0 deletions modules/media_exchange/media_exchange.c
Expand Up @@ -34,6 +34,8 @@ static int media_fork_to_uri(struct sip_msg *msg, str *uri,
int leg, str *headers, int *medianum);
static int media_fork_from_call(struct sip_msg *msg, str *callid,
int leg, int *medianum);
static int media_fork_pause(struct sip_msg *msg, int leg, int *medianum);
static int media_fork_resume(struct sip_msg *msg, int leg, int *medianum);
static int media_exchange_from_uri(struct sip_msg *msg, str *uri,
int leg, str *body, str *headers, int *nohold);
static int media_exchange_to_call(struct sip_msg *msg, str *callid,
Expand Down Expand Up @@ -101,6 +103,16 @@ static cmd_export_t cmds[] = {
{CMD_PARAM_INT|CMD_PARAM_OPT,0,0}, /* medianum */
{0,0,0}},
REQUEST_ROUTE},
{"media_fork_pause",(cmd_function)media_fork_pause, {
{CMD_PARAM_STR|CMD_PARAM_OPT,fixup_media_leg_both,0}, /* leg */
{CMD_PARAM_INT|CMD_PARAM_OPT,0,0}, /* medianum */
{0,0,0}},
REQUEST_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|EVENT_ROUTE},
{"media_fork_resume",(cmd_function)media_fork_resume, {
{CMD_PARAM_STR|CMD_PARAM_OPT,fixup_media_leg_both,0}, /* leg */
{CMD_PARAM_INT|CMD_PARAM_OPT,0,0}, /* medianum */
{0,0,0}},
REQUEST_ROUTE|BRANCH_ROUTE|ONREPLY_ROUTE|EVENT_ROUTE},
{"media_terminate",(cmd_function)media_terminate, {
{CMD_PARAM_STR|CMD_PARAM_OPT,fixup_media_leg,0}, /* leg */
{CMD_PARAM_INT|CMD_PARAM_OPT,0,0}, /* nohold */
Expand Down Expand Up @@ -1014,6 +1026,112 @@ static int media_send_fail(struct cell *t, struct dlg_cell *dlg, int leg)
return ret;
}

static int media_fork_pause(struct sip_msg *msg, int leg, int *medianum)
{
struct dlg_cell *dlg;
struct media_session *ms;
struct media_session_leg *msl;
int ret = 0, n;

dlg = media_dlg.get_dlg();
if (!dlg) {
LM_WARN("dialog does not exist! please engage this function "
"after creating/matching the dialog!\n");
return -1;
}

ms = media_session_get(dlg);
if (!ms) {
LM_WARN("could not find media session for dialog %.*s\n",
dlg->callid.len, dlg->callid.s);
return -1;
}
if (leg == MEDIA_LEG_UNSPEC) {
for (msl = ms->legs; msl; msl = msl->next)
if (msl->type == MEDIA_SESSION_TYPE_FORK) {
n = media_fork_pause_leg(msl, medianum?*medianum:-1);
if (n > 0) {
if (media_session_fork_update(msl) == 0)
ret+=n;
else
LM_ERR("could not update media session leg!\n");
}
}
} else {
msl = media_session_get_leg(ms, leg);
if (!msl) {
LM_WARN("media session leg %d does not exist!\n", leg);
return -1;
}
n = media_fork_pause_leg(msl, medianum?*medianum:-1);
if (n > 0) {
if (media_session_fork_update(msl) == 0)
ret=n;
else
LM_ERR("could not update media session leg!\n");
}
}

if (ret == 0) {
LM_DBG("no sessions to resume!\n");
return -1;
}
return ret;
}

static int media_fork_resume(struct sip_msg *msg, int leg, int *medianum)
{
struct dlg_cell *dlg;
struct media_session *ms;
struct media_session_leg *msl;
int ret = 0, n;

dlg = media_dlg.get_dlg();
if (!dlg) {
LM_WARN("dialog does not exist! please engage this function "
"after creating/matching the dialog!\n");
return -1;
}

ms = media_session_get(dlg);
if (!ms) {
LM_WARN("could not find media session for dialog %.*s\n",
dlg->callid.len, dlg->callid.s);
return -1;
}
if (leg == MEDIA_LEG_UNSPEC) {
for (msl = ms->legs; msl; msl = msl->next)
if (msl->type == MEDIA_SESSION_TYPE_FORK) {
n = media_fork_resume_leg(msl, medianum?*medianum:-1);
if (n > 0) {
if (media_session_fork_update(msl) == 0)
ret+=n;
else
LM_ERR("could not update media session leg!\n");
}
}
} else {
msl = media_session_get_leg(ms, leg);
if (!msl) {
LM_WARN("media session leg %d does not exist!\n", leg);
return -1;
}
n = media_fork_resume_leg(msl, medianum?*medianum:-1);
if (n > 0) {
if (media_session_fork_update(msl) == 0)
ret=n;
else
LM_ERR("could not update media session leg!\n");
}
}

if (ret == 0) {
LM_DBG("no sessions to resume!\n");
return -1;
}
return ret;
}

static int handle_media_session_reply_exchange(struct media_session_leg *msl,
str *body, struct media_session_tm_param *p)
{
Expand Down
83 changes: 67 additions & 16 deletions modules/media_exchange/media_utils.c
Expand Up @@ -27,7 +27,8 @@ str content_type_sdp_hdr = str_init("Content-Type: application/sdp\r\n");
enum media_fork_state {
MEDIA_FORK_INIT,
MEDIA_FORK_ON,
MEDIA_FORK_OFF
MEDIA_FORK_OFF,
MEDIA_FORK_CLOSE,
};

struct media_fork_info {
Expand Down Expand Up @@ -277,6 +278,43 @@ static int media_fork_stream_disable(sdp_stream_cell_t *stream)
return 0;
}

int media_fork_pause_leg(struct media_session_leg *msl, int medianum)
{
struct media_fork_info *mf;
int ret = 0;

for (mf = msl->params; mf; mf = mf->next) {
if (medianum < 0 || medianum == mf->medianum) {
if (mf->state != MEDIA_FORK_ON)
continue;
mf->state = MEDIA_FORK_CLOSE;
ret++;
if (medianum >= 0)
break;
}
}
return ret;
}

int media_fork_resume_leg(struct media_session_leg *msl, int medianum)
{
struct media_fork_info *mf;
int ret = 0;

for (mf = msl->params; mf; mf = mf->next) {
if (medianum < 0 || medianum == mf->medianum) {
if (mf->state == MEDIA_FORK_INIT || mf->state == MEDIA_FORK_ON)
continue;
mf->state = MEDIA_FORK_INIT;
ret++;
if (medianum >= 0)
break;
}
}
return ret;
}


static int media_fork_prepare_body(void)
{
str tmp;
Expand Down Expand Up @@ -858,24 +896,33 @@ void media_forks_free(struct media_fork_info *mf)
int media_fork_update(struct media_session_leg *msl,
struct media_fork_info *mf, str *ip, str *port, int disabled)
{
if (mf->state == MEDIA_FORK_OFF) {
switch (mf->state) {
case MEDIA_FORK_CLOSE:
/* we don't care right now, stream is off anyway !
* we update it when it gets resumed*/
if (media_nofork(msl->ms->dlg, mf) == 0)
return 1;
break;
case MEDIA_FORK_OFF:
/* we don't care right now, stream is off anyway !
* we update it when it gets resumed*/
return 1;
} else {
case MEDIA_FORK_ON:
/* stream should be enabled */
if (disabled)
return 0;
if (mf->state == MEDIA_FORK_ON) {
/* there's an ongoing forking happening - drop if changed */
if (media_fork_cmp(mf, ip, port)) {
/* same thing - leave it like this */
return 1;
} else {
/* disable previous forking */
media_nofork(msl->ms->dlg, mf);
}
/* there's an ongoing forking happening - drop if changed */
if (media_fork_cmp(mf, ip, port)) {
/* same thing - leave it like this */
return 1;
} else {
/* disable previous forking */
media_nofork(msl->ms->dlg, mf);
}
/* fallback to init */
case MEDIA_FORK_INIT:
if (disabled)
return 0;
/* here it is INIT or OFF */
media_fork_fill(mf, ip, port);
if (media_fork(msl->ms->dlg, mf) == 0)
Expand Down Expand Up @@ -932,6 +979,7 @@ int media_session_fork_update(struct media_session_leg *msl)
struct media_fork_info *mf;
media_util_init_static();
int media_idx = 1;
int media_disabled;
sdp_session_cell_t *session;
sdp_stream_cell_t *stream;
sdp_info_t *sdp;
Expand Down Expand Up @@ -962,15 +1010,18 @@ int media_session_fork_update(struct media_session_leg *msl)
else
sdp = &ms_util_sdp1;
for (session = sdp->sessions; session; session = session->next)
for (stream = session->streams; stream; stream = stream->next)
media_fork_add_stream(stream, (mf->state == MEDIA_FORK_OFF?1:0));
for (stream = session->streams; stream; stream = stream->next) {
if (mf->state == MEDIA_FORK_OFF || mf->state == MEDIA_FORK_CLOSE)
media_disabled = 1;
else
media_disabled = 0;
media_fork_add_stream(stream, media_disabled);
}
}
if (media_idx != 1) {
if (media_session_req(msl, "INVITE", MS_UTIL_BUF_STR) < 0) {
LM_ERR("could not challenge media server!\n");
ret = -3;
} else {
msl->state = MEDIA_SESSION_STATE_UPDATE;
}
}
ret = 0;
Expand Down
4 changes: 4 additions & 0 deletions modules/media_exchange/media_utils.h
Expand Up @@ -51,6 +51,10 @@ struct media_fork_info *media_fork_search(struct media_fork_info *mf, int search

int media_forks_stop(struct media_session_leg *msl);

int media_fork_pause_leg(struct media_session_leg *msl, int medianum);

int media_fork_resume_leg(struct media_session_leg *msl, int medianum);

int media_fork_body_update(struct media_session_leg *ml, str *body, int leg);

int media_session_fork_update(struct media_session_leg *msl);
Expand Down

0 comments on commit 2686630

Please sign in to comment.