summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/app_queue.c239
-rw-r--r--channels/chan_sip.c44
-rw-r--r--configs/samples/queuerules.conf.sample21
-rw-r--r--res/res_format_attr_h263.c2
-rw-r--r--res/res_format_attr_h264.c2
-rw-r--r--res/res_rtp_asterisk.c69
6 files changed, 248 insertions, 129 deletions
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 0f46d280c..f158a4caa 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -1545,6 +1545,7 @@ struct queue_ent {
int pending; /*!< Non-zero if we are attempting to call a member */
int max_penalty; /*!< Limit the members that can take this call to this penalty or lower */
int min_penalty; /*!< Limit the members that can take this call to this penalty or higher */
+ int raise_penalty; /*!< Float lower penalty mambers to a minimum penalty */
int linpos; /*!< If using linear strategy, what position are we at? */
int linwrapped; /*!< Is the linpos wrapped? */
time_t start; /*!< When we started holding */
@@ -1570,10 +1571,11 @@ struct member {
int paused; /*!< Are we paused (not accepting calls)? */
char reason_paused[80]; /*!< Reason of paused if member is paused */
int queuepos; /*!< In what order (pertains to certain strategies) should this member be called? */
+ int callcompletedinsl; /*!< Whether the current call was completed within service level */
+ time_t starttime; /*!< The time at which the member answered the current caller. */
time_t lastcall; /*!< When last successful call was hungup */
time_t lastpause; /*!< When started the last pause */
- unsigned int in_call:1; /*!< True if member is still in call. (so lastcall is not actual) */
- struct call_queue *lastqueue; /*!< Last queue we received a call */
+ struct call_queue *lastqueue; /*!< Last queue we received a call */
unsigned int dead:1; /*!< Used to detect members deleted in realtime */
unsigned int delme:1; /*!< Flag to delete entry on reload */
char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
@@ -1605,8 +1607,10 @@ struct penalty_rule {
int time; /*!< Number of seconds that need to pass before applying this rule */
int max_value; /*!< The amount specified in the penalty rule for max penalty */
int min_value; /*!< The amount specified in the penalty rule for min penalty */
+ int raise_value; /*!< The amount specified in the penalty rule for min penalty */
int max_relative; /*!< Is the max adjustment relative? 1 for relative, 0 for absolute */
int min_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
+ int raise_relative; /*!< Is the min adjustment relative? 1 for relative, 0 for absolute */
AST_LIST_ENTRY(penalty_rule) list; /*!< Next penalty_rule */
};
@@ -1729,6 +1733,7 @@ static struct ao2_container *queues;
static void update_realtime_members(struct call_queue *q);
static struct member *interface_exists(struct call_queue *q, const char *interface);
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
+static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime);
static struct member *find_member_by_queuename_and_interface(const char *queuename, const char *interface);
/*! \brief sets the QUEUESTATUS channel variable */
@@ -2220,7 +2225,7 @@ static struct ast_json *queue_member_blob_create(struct call_queue *q, struct me
"CallsTaken", mem->calls,
"LastCall", (int)mem->lastcall,
"LastPause", (int)mem->lastpause,
- "InCall", mem->in_call,
+ "InCall", mem->starttime ? 1 : 0,
"Status", mem->status,
"Paused", mem->paused,
"PausedReason", mem->reason_paused,
@@ -2233,7 +2238,7 @@ static struct ast_json *queue_member_blob_create(struct call_queue *q, struct me
* is available, the function immediately returns 0. If no members are available,
* then -1 is returned.
*/
-static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions, int devstate)
+static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
{
struct member *member;
struct ao2_iterator mem_iter;
@@ -2241,7 +2246,12 @@ static int get_member_status(struct call_queue *q, int max_penalty, int min_pena
ao2_lock(q);
mem_iter = ao2_iterator_init(q->members, 0);
for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
- if ((max_penalty != INT_MAX && member->penalty > max_penalty) || (min_penalty != INT_MAX && member->penalty < min_penalty)) {
+ int penalty = member->penalty;
+ if (raise_penalty != INT_MAX && penalty < raise_penalty) {
+ ast_debug(4, "%s is having his penalty raised up from %d to %d\n", member->membername, penalty, raise_penalty);
+ penalty = raise_penalty;
+ }
+ if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
if (conditions & QUEUE_EMPTY_PENALTY) {
ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
continue;
@@ -2284,10 +2294,6 @@ static int get_member_status(struct call_queue *q, int max_penalty, int min_pena
if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
break;
- } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->in_call && q->wrapuptime) {
- ast_debug(4, "%s is unavailable because still in call, so we can`t check "
- "wrapuptime (%d)\n", member->membername, q->wrapuptime);
- break;
} else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime);
break;
@@ -2306,7 +2312,7 @@ static int get_member_status(struct call_queue *q, int max_penalty, int min_pena
if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) {
/* member state still may be RINGING due to lag in event message - check again with device state */
- return get_member_status(q, max_penalty, min_penalty, conditions, 1);
+ return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1);
}
return -1;
}
@@ -2384,6 +2390,14 @@ static void pending_members_remove(struct member *mem)
static void update_status(struct call_queue *q, struct member *m, const int status)
{
if (m->status != status) {
+ /* If this member has transitioned to being available then update their queue
+ * information. If they are currently in a call then the leg to the agent will be
+ * considered done and the call finished.
+ */
+ if (status == AST_DEVICE_NOT_INUSE) {
+ update_queue(q, m, m->callcompletedinsl, m->starttime);
+ }
+
m->status = status;
/* Remove the member from the pending members pool only when the status changes.
@@ -2430,9 +2444,6 @@ static int is_member_available(struct call_queue *q, struct member *mem)
}
/* Let wrapuptimes override device state availability */
- if (q->wrapuptime && mem->in_call) {
- available = 0; /* member is still in call, cant check wrapuptime to lastcall time */
- }
if (mem->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < mem->lastcall)) {
available = 0;
}
@@ -2791,8 +2802,9 @@ static void clear_queue(struct call_queue *q)
struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
while ((mem = ao2_iterator_next(&mem_iter))) {
mem->calls = 0;
+ mem->callcompletedinsl = 0;
mem->lastcall = 0;
- mem->in_call = 0;
+ mem->starttime = 0;
ao2_ref(mem, -1);
}
ao2_iterator_destroy(&mem_iter);
@@ -2810,7 +2822,7 @@ static void clear_queue(struct call_queue *q)
*/
static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
{
- char *timestr, *maxstr, *minstr, *contentdup;
+ char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
struct penalty_rule *rule = NULL, *rule_iter;
struct rule_list *rl_iter;
int penaltychangetime, inserted = 0;
@@ -2828,8 +2840,16 @@ static int insert_penaltychange(const char *list_name, const char *content, cons
}
*maxstr++ = '\0';
- timestr = contentdup;
+ if ((minstr = strchr(maxstr,','))) {
+ *minstr++ = '\0';
+ if ((raisestr = strchr(minstr,','))) {
+ *raisestr++ = '\0';
+ }
+ } else {
+ raisestr = NULL;
+ }
+ timestr = contentdup;
if ((penaltychangetime = atoi(timestr)) < 0) {
ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
ast_free(rule);
@@ -2838,10 +2858,6 @@ static int insert_penaltychange(const char *list_name, const char *content, cons
rule->time = penaltychangetime;
- if ((minstr = strchr(maxstr,','))) {
- *minstr++ = '\0';
- }
-
/* The last check will evaluate true if either no penalty change is indicated for a given rule
* OR if a min penalty change is indicated but no max penalty change is */
if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
@@ -2859,6 +2875,15 @@ static int insert_penaltychange(const char *list_name, const char *content, cons
rule->min_relative = 1;
}
+ if (!ast_strlen_zero(raisestr)) {
+ if (*raisestr == '+' || *raisestr == '-') {
+ rule->raise_relative = 1;
+ }
+ rule->raise_value = atoi(raisestr);
+ } else { /*there was no raise specified, so assume this means no change*/
+ rule->raise_relative = 1;
+ }
+
/*We have the rule made, now we need to insert it where it belongs*/
AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
if (strcasecmp(rl_iter->name, list_name)) {
@@ -2915,9 +2940,10 @@ static int load_realtime_rules(void)
return 0;
}
while ((rulecat = ast_category_browse(cfg, rulecat))) {
- const char *timestr, *maxstr, *minstr, *rule_name;
+ const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
int penaltychangetime, rule_exists = 0, inserted = 0;
- int max_penalty = 0, min_penalty = 0, min_relative = 0, max_relative = 0;
+ int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
+ int min_relative = 0, max_relative = 0, raise_relative = 0;
struct penalty_rule *new_penalty_rule = NULL;
rule_name = ast_variable_retrieve(cfg, rulecat, "rule_name");
@@ -2968,11 +2994,22 @@ static int load_realtime_rules(void)
min_relative = 1;
}
}
+ if (!(raisestr = ast_variable_retrieve(cfg, rulecat, "raise_penalty")) ||
+ ast_strlen_zero(raisestr) || sscanf(raisestr, "%30d", &raise_penalty) != 1) {
+ raise_penalty = 0;
+ raise_relative = 1;
+ } else {
+ if (*raisestr == '+' || *raisestr == '-') {
+ raise_relative = 1;
+ }
+ }
new_penalty_rule->time = penaltychangetime;
new_penalty_rule->max_relative = max_relative;
new_penalty_rule->max_value = max_penalty;
new_penalty_rule->min_relative = min_relative;
new_penalty_rule->min_value = min_penalty;
+ new_penalty_rule->raise_relative = raise_relative;
+ new_penalty_rule->raise_value = raise_penalty;
AST_LIST_TRAVERSE_SAFE_BEGIN(&new_rl->rules, pr_iter, list) {
if (new_penalty_rule->time < pr_iter->time) {
AST_LIST_INSERT_BEFORE_CURRENT(new_penalty_rule, list);
@@ -3714,7 +3751,7 @@ static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *
/* This is our one */
if (q->joinempty) {
int status = 0;
- if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty, 0))) {
+ if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, qe->raise_penalty, q->joinempty, 0))) {
*reason = QUEUE_JOINEMPTY;
ao2_unlock(q);
queue_t_unref(q, "Done with realtime queue");
@@ -4275,12 +4312,6 @@ static int can_ring_entry(struct queue_ent *qe, struct callattempt *call)
return 0;
}
- if (call->member->in_call && call->lastqueue && call->lastqueue->wrapuptime) {
- ast_debug(1, "%s is in call, so not available (wrapuptime %d)\n",
- call->interface, call->lastqueue->wrapuptime);
- return 0;
- }
-
if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime))
|| (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) {
ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
@@ -5429,6 +5460,32 @@ static void update_qe_rule(struct queue_ent *qe)
qe->min_penalty, ast_channel_name(qe->chan), qe->pr->time);
}
+ if (qe->raise_penalty != INT_MAX) {
+ char raise_penalty_str[20];
+ int raise_penalty;
+
+ if (qe->pr->raise_relative) {
+ raise_penalty = qe->raise_penalty + qe->pr->raise_value;
+ } else {
+ raise_penalty = qe->pr->raise_value;
+ }
+
+ /* a relative change to the penalty could put it below 0 */
+ if (raise_penalty < 0) {
+ raise_penalty = 0;
+ }
+
+ if (max_penalty != INT_MAX && raise_penalty > max_penalty) {
+ raise_penalty = max_penalty;
+ }
+
+ snprintf(raise_penalty_str, sizeof(raise_penalty_str), "%d", raise_penalty);
+ pbx_builtin_setvar_helper(qe->chan, "QUEUE_RAISE_PENALTY", raise_penalty_str);
+ qe->raise_penalty = raise_penalty;
+ ast_debug(3, "Setting raised penalty to %d for caller %s since %d seconds have elapsed\n",
+ qe->raise_penalty, ast_channel_name(qe->chan), qe->pr->time);
+ }
+
qe->pr = AST_LIST_NEXT(qe->pr, list);
}
@@ -5462,7 +5519,7 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
if (qe->parent->leavewhenempty) {
int status = 0;
- if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty, 0))) {
+ if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->raise_penalty, qe->parent->leavewhenempty, 0))) {
*reason = QUEUE_LEAVEEMPTY;
ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) (time(NULL) - qe->start));
res = -1;
@@ -5522,14 +5579,22 @@ static int wait_our_turn(struct queue_ent *qe, int ringing, enum queue_result *r
* \brief update the queue status
* \retval Always 0
*/
-static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, int newtalktime)
+static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
{
int oldtalktime;
-
+ int newtalktime = time(NULL) - starttime;
struct member *mem;
struct call_queue *qtmp;
struct ao2_iterator queue_iter;
+ /* It is possible for us to be called when a call has already been considered terminated
+ * and data updated, so to ensure we only act on the call that the agent is currently in
+ * we check when the call was bridged.
+ */
+ if (!starttime || (member->starttime != starttime)) {
+ return 0;
+ }
+
if (shared_lastcall) {
queue_iter = ao2_iterator_init(queues, 0);
while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
@@ -5537,10 +5602,9 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom
if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
time(&mem->lastcall);
mem->calls++;
+ mem->callcompletedinsl = 0;
+ mem->starttime = 0;
mem->lastqueue = q;
- mem->in_call = 0;
- ast_debug(4, "Marked member %s as NOT in_call. Lastcall time: %ld \n",
- mem->membername, (long)mem->lastcall);
ao2_ref(mem, -1);
}
ao2_unlock(qtmp);
@@ -5550,11 +5614,10 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom
} else {
ao2_lock(q);
time(&member->lastcall);
+ member->callcompletedinsl = 0;
member->calls++;
+ member->starttime = 0;
member->lastqueue = q;
- member->in_call = 0;
- ast_debug(4, "Marked member %s as NOT in_call. Lastcall time: %ld \n",
- member->membername, (long)member->lastcall);
ao2_unlock(q);
}
/* Member might never experience any direct status change (local
@@ -5593,10 +5656,15 @@ static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct
/* disregarding penalty on too few members? */
int membercount = ao2_container_count(q->members);
unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
+ int penalty = mem->penalty;
if (usepenalty) {
- if ((qe->max_penalty != INT_MAX && mem->penalty > qe->max_penalty) ||
- (qe->min_penalty != INT_MAX && mem->penalty < qe->min_penalty)) {
+ if (qe->raise_penalty != INT_MAX && penalty < qe->raise_penalty) {
+ /* Low penalty is raised up to the current minimum */
+ penalty = qe->raise_penalty;
+ }
+ if ((qe->max_penalty != INT_MAX && penalty > qe->max_penalty) ||
+ (qe->min_penalty != INT_MAX && penalty < qe->min_penalty)) {
return -1;
}
} else {
@@ -5607,7 +5675,7 @@ static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct
switch (q->strategy) {
case QUEUE_STRATEGY_RINGALL:
/* Everyone equal, except for penalty */
- tmp->metric = mem->penalty * 1000000 * usepenalty;
+ tmp->metric = penalty * 1000000 * usepenalty;
break;
case QUEUE_STRATEGY_LINEAR:
if (pos < qe->linpos) {
@@ -5619,7 +5687,7 @@ static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct
}
tmp->metric = pos;
}
- tmp->metric += mem->penalty * 1000000 * usepenalty;
+ tmp->metric += penalty * 1000000 * usepenalty;
break;
case QUEUE_STRATEGY_RRORDERED:
case QUEUE_STRATEGY_RRMEMORY:
@@ -5633,18 +5701,18 @@ static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct
}
tmp->metric = pos;
}
- tmp->metric += mem->penalty * 1000000 * usepenalty;
+ tmp->metric += penalty * 1000000 * usepenalty;
break;
case QUEUE_STRATEGY_RANDOM:
tmp->metric = ast_random() % 1000;
- tmp->metric += mem->penalty * 1000000 * usepenalty;
+ tmp->metric += penalty * 1000000 * usepenalty;
break;
case QUEUE_STRATEGY_WRANDOM:
- tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
+ tmp->metric = ast_random() % ((1 + penalty) * 1000);
break;
case QUEUE_STRATEGY_FEWESTCALLS:
tmp->metric = mem->calls;
- tmp->metric += mem->penalty * 1000000 * usepenalty;
+ tmp->metric += penalty * 1000000 * usepenalty;
break;
case QUEUE_STRATEGY_LEASTRECENT:
if (!mem->lastcall) {
@@ -5652,7 +5720,7 @@ static int calc_metric(struct call_queue *q, struct member *mem, int pos, struct
} else {
tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
}
- tmp->metric += mem->penalty * 1000000 * usepenalty;
+ tmp->metric += penalty * 1000000 * usepenalty;
break;
default:
ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
@@ -6012,7 +6080,7 @@ static void handle_blind_transfer(void *userdata, struct stasis_subscription *su
send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
queue_data->holdstart, queue_data->starttime, TRANSFER);
update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
- time(NULL) - queue_data->starttime);
+ queue_data->starttime);
remove_stasis_subscriptions(queue_data);
}
@@ -6072,7 +6140,7 @@ static void handle_attended_transfer(void *userdata, struct stasis_subscription
send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
queue_data->holdstart, queue_data->starttime, TRANSFER);
update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
- time(NULL) - queue_data->starttime);
+ queue_data->starttime);
remove_stasis_subscriptions(queue_data);
}
@@ -6273,7 +6341,7 @@ static void handle_hangup(void *userdata, struct stasis_subscription *sub,
send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
queue_data->holdstart, queue_data->starttime, reason);
update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
- time(NULL) - queue_data->starttime);
+ queue_data->starttime);
remove_stasis_subscriptions(queue_data);
}
@@ -6534,7 +6602,6 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
int x=0;
char *announce = NULL;
char digit = 0;
- time_t callstart;
time_t now = time(NULL);
struct ast_bridge_config bridge_config;
char nondataquality = 1;
@@ -6545,12 +6612,10 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
char tmpid[256];
int forwardsallowed = 1;
int block_connected_line = 0;
- int callcompletedinsl;
struct ao2_iterator memi;
struct queue_end_bridge *queue_end_bridge = NULL;
- struct ao2_iterator queue_iter; /* to iterate through all queues (for shared_lastcall)*/
- struct member *mem;
- struct call_queue *queuetmp;
+ int callcompletedinsl;
+ time_t starttime;
memset(&bridge_config, 0, sizeof(bridge_config));
tmpid[0] = 0;
@@ -6739,10 +6804,10 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
/* Update parameters for the queue */
time(&now);
recalc_holdtime(qe, (now - qe->start));
+ member = lpeer->member;
ao2_lock(qe->parent);
- callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
+ callcompletedinsl = member->callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
ao2_unlock(qe->parent);
- member = lpeer->member;
/* Increment the refcount for this member, since we're going to be using it for awhile in here. */
ao2_ref(member, 1);
hangupcalls(qe, outgoing, peer, qe->cancel_answered_elsewhere);
@@ -6978,27 +7043,6 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
}
qe->handled++;
- /** mark member as "in_call" in all queues */
- if (shared_lastcall) {
- queue_iter = ao2_iterator_init(queues, 0);
- while ((queuetmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
- ao2_lock(queuetmp);
- if ((mem = ao2_find(queuetmp->members, member, OBJ_POINTER))) {
- mem->in_call = 1;
- ast_debug(4, "Marked member %s as in_call \n", mem->membername);
- ao2_ref(mem, -1);
- }
- ao2_unlock(queuetmp);
- queue_t_unref(queuetmp, "Done with iterator");
- }
- ao2_iterator_destroy(&queue_iter);
- } else {
- ao2_lock(qe->parent);
- member->in_call = 1;
- ast_debug(4, "Marked member %s as in_call \n", member->membername);
- ao2_unlock(qe->parent);
- }
-
ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) (time(NULL) - qe->start), ast_channel_uniqueid(peer),
(long)(orig - to > 0 ? (orig - to) / 1000 : 0));
@@ -7026,8 +7070,16 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a
queue_t_ref(qe->parent, "For bridge_config reference");
}
- time(&callstart);
- setup_stasis_subs(qe, peer, member, qe->start, callstart, callcompletedinsl);
+ ao2_lock(qe->parent);
+ time(&member->starttime);
+ starttime = member->starttime;
+ ao2_unlock(qe->parent);
+ /* As a queue member may end up in multiple calls at once if a transfer occurs with
+ * a Local channel in the mix we pass the current call information (starttime) to the
+ * Stasis subscriptions so when they update the queue member data it becomes a noop
+ * if this call is no longer between the caller and the queue member.
+ */
+ setup_stasis_subs(qe, peer, member, qe->start, starttime, callcompletedinsl);
bridge = ast_bridge_call_with_flags(qe->chan, peer, &bridge_config,
AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM | AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM);
@@ -7916,8 +7968,10 @@ static void copy_rules(struct queue_ent *qe, const char *rulename)
new_pr->time = pr_iter->time;
new_pr->max_value = pr_iter->max_value;
new_pr->min_value = pr_iter->min_value;
+ new_pr->raise_value = pr_iter->raise_value;
new_pr->max_relative = pr_iter->max_relative;
new_pr->min_relative = pr_iter->min_relative;
+ new_pr->raise_relative = pr_iter->raise_relative;
AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
}
}
@@ -7943,9 +7997,10 @@ static int queue_exec(struct ast_channel *chan, const char *data)
const char *user_priority;
const char *max_penalty_str;
const char *min_penalty_str;
+ const char *raise_penalty_str;
int prio;
int qcontinue = 0;
- int max_penalty, min_penalty;
+ int max_penalty, min_penalty, raise_penalty;
enum queue_result reason = QUEUE_UNKNOWN;
/* whether to exit Queue application after the timeout hits */
int tries = 0;
@@ -8057,6 +8112,18 @@ static int queue_exec(struct ast_channel *chan, const char *data)
} else {
min_penalty = INT_MAX;
}
+
+ if ((raise_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_RAISE_PENALTY"))) {
+ if (sscanf(raise_penalty_str, "%30d", &raise_penalty) == 1) {
+ ast_debug(1, "%s: Got raise penalty %d from ${QUEUE_RAISE_PENALTY}.\n", ast_channel_name(chan), raise_penalty);
+ } else {
+ ast_log(LOG_WARNING, "${QUEUE_RAISE_PENALTY}: Invalid value (%s), channel %s.\n",
+ raise_penalty_str, ast_channel_name(chan));
+ raise_penalty = INT_MAX;
+ }
+ } else {
+ raise_penalty = INT_MAX;
+ }
ast_channel_unlock(chan);
if (ast_test_flag(&opts, OPT_RINGING)) {
@@ -8086,6 +8153,7 @@ static int queue_exec(struct ast_channel *chan, const char *data)
qe.prio = prio;
qe.max_penalty = max_penalty;
qe.min_penalty = min_penalty;
+ qe.raise_penalty = raise_penalty;
qe.last_pos_said = 0;
qe.last_pos = 0;
qe.last_periodic_announce_time = time(NULL);
@@ -8173,7 +8241,7 @@ check_turns:
if (qe.parent->leavewhenempty) {
int status = 0;
- if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty, 0))) {
+ if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.raise_penalty, qe.parent->leavewhenempty, 0))) {
record_abandoned(&qe);
reason = QUEUE_LEAVEEMPTY;
ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
@@ -9469,7 +9537,7 @@ static char *__queues_show(struct mansession *s, int fd, int argc, const char *
ast_str_append(&out, 0, "%s%s%s%s%s%s%s%s%s",
mem->dynamic ? ast_term_color(COLOR_CYAN, COLOR_BLACK) : "", mem->dynamic ? " (dynamic)" : "", ast_term_reset(),
mem->realtime ? ast_term_color(COLOR_MAGENTA, COLOR_BLACK) : "", mem->realtime ? " (realtime)" : "", ast_term_reset(),
- mem->in_call ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->in_call ? " (in call)" : "", ast_term_reset());
+ mem->starttime ? ast_term_color(COLOR_BROWN, COLOR_BLACK) : "", mem->starttime ? " (in call)" : "", ast_term_reset());
if (mem->paused) {
if (ast_strlen_zero(mem->reason_paused)) {
ast_str_append(&out, 0, " %s(paused was %ld secs ago)%s",
@@ -9860,7 +9928,7 @@ static int manager_queues_status(struct mansession *s, const struct message *m)
"%s"
"\r\n",
q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static",
- mem->penalty, mem->calls, (int)mem->lastcall, (int)mem->lastpause, mem->in_call, mem->status,
+ mem->penalty, mem->calls, (int)mem->lastcall, (int)mem->lastpause, mem->starttime ? 1 : 0, mem->status,
mem->paused, mem->reason_paused, idText);
++q_items;
}
@@ -10622,7 +10690,7 @@ static char *handle_queue_rule_show(struct ast_cli_entry *e, int cmd, struct ast
if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
- ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value);
+ ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d, adjust QUEUE_MIN_PENALTY %s %d and adjust QUEUE_RAISE_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value, pr_iter->raise_relative ? "by" : "to", pr_iter->raise_value);
}
}
}
@@ -10920,6 +10988,7 @@ AST_DATA_STRUCTURE(member, DATA_EXPORT_MEMBER);
MEMBER(queue_ent, pending, AST_DATA_INTEGER) \
MEMBER(queue_ent, max_penalty, AST_DATA_INTEGER) \
MEMBER(queue_ent, min_penalty, AST_DATA_INTEGER) \
+ MEMBER(queue_ent, raise_penalty, AST_DATA_INTEGER) \
MEMBER(queue_ent, linpos, AST_DATA_INTEGER) \
MEMBER(queue_ent, linwrapped, AST_DATA_INTEGER) \
MEMBER(queue_ent, start, AST_DATA_INTEGER) \
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 930dc0f67..aaeb01e13 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1215,7 +1215,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
static int process_sdp_o(const char *o, struct sip_pvt *p);
static int process_sdp_c(const char *c, struct ast_sockaddr *addr);
static int process_sdp_a_sendonly(const char *a, int *sendonly);
-static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
+static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance, int rtcp_mux);
static int process_sdp_a_rtcp_mux(const char *a, struct sip_pvt *p, int *requested);
static int process_sdp_a_dtls(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newaudiortp, int *last_rtpmap_codec);
@@ -10128,6 +10128,24 @@ static void set_ice_components(struct sip_pvt *p, struct ast_rtp_instance *insta
}
}
+static int has_media_level_attribute(int start, struct sip_request *req, const char *attr)
+{
+ int next = start;
+ char type;
+ const char *value;
+
+ /* We don't care about the return result here */
+ get_sdp_iterate(&next, req, "m");
+
+ while ((type = get_sdp_line(&start, next, req, &value)) != '\0') {
+ if (type == 'a' && !strcasecmp(value, attr)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/*! \brief Process SIP SDP offer, select formats and activate media channels
If offer is rejected, we will not change any properties of the call
Return 0 on success, a negative value on errors.
@@ -10270,13 +10288,13 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
else if (process_sdp_a_image(value, p))
processed = TRUE;
- if (process_sdp_a_ice(value, p, p->rtp)) {
+ if (process_sdp_a_ice(value, p, p->rtp, 0)) {
processed = TRUE;
}
- if (process_sdp_a_ice(value, p, p->vrtp)) {
+ if (process_sdp_a_ice(value, p, p->vrtp, 0)) {
processed = TRUE;
}
- if (process_sdp_a_ice(value, p, p->trtp)) {
+ if (process_sdp_a_ice(value, p, p->trtp, 0)) {
processed = TRUE;
}
@@ -10316,6 +10334,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
int image = FALSE;
int text = FALSE;
int processed_crypto = FALSE;
+ int rtcp_mux_offered = 0;
char protocol[18] = {0,};
unsigned int x;
struct ast_rtp_engine_dtls *dtls;
@@ -10335,6 +10354,9 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
AST_LIST_INSERT_TAIL(&p->offered_media, offer, next);
offer->type = SDP_UNKNOWN;
+ /* We need to check for this ahead of time */
+ rtcp_mux_offered = has_media_level_attribute(iterator, req, "rtcp-mux");
+
/* Check for 'audio' media offer */
if (strncmp(m, "audio ", 6) == 0) {
if ((sscanf(m, "audio %30u/%30u %17s %n", &x, &numberofports, protocol, &len) == 3 && len > 0) ||
@@ -10701,7 +10723,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
case 'a':
/* Audio specific scanning */
if (audio) {
- if (process_sdp_a_ice(value, p, p->rtp)) {
+ if (process_sdp_a_ice(value, p, p->rtp, rtcp_mux_offered)) {
processed = TRUE;
} else if (process_sdp_a_dtls(value, p, p->rtp)) {
processed_crypto = TRUE;
@@ -10726,7 +10748,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
}
/* Video specific scanning */
else if (video) {
- if (process_sdp_a_ice(value, p, p->vrtp)) {
+ if (process_sdp_a_ice(value, p, p->vrtp, rtcp_mux_offered)) {
processed = TRUE;
} else if (process_sdp_a_dtls(value, p, p->vrtp)) {
processed_crypto = TRUE;
@@ -10749,7 +10771,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
}
/* Text (T.140) specific scanning */
else if (text) {
- if (process_sdp_a_ice(value, p, p->trtp)) {
+ if (process_sdp_a_ice(value, p, p->trtp, rtcp_mux_offered)) {
processed = TRUE;
} else if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &last_rtpmap_codec)) {
processed = TRUE;
@@ -11271,7 +11293,7 @@ static int process_sdp_a_sendonly(const char *a, int *sendonly)
return found;
}
-static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance)
+static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance, int rtcp_mux_offered)
{
struct ast_rtp_engine_ice *ice;
int found = FALSE;
@@ -11291,6 +11313,12 @@ static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_in
found = TRUE;
} else if (sscanf(a, "candidate: %31s %30u %3s %30u %23s %30u typ %5s %*s %23s %*s %30u", foundation, &candidate.id, transport, (unsigned *)&candidate.priority,
address, &port, cand_type, relay_address, &relay_port) >= 7) {
+
+ if (rtcp_mux_offered && ast_test_flag(&p->flags[2], SIP_PAGE3_RTCP_MUX) && candidate.id > 1) {
+ /* If we support RTCP-MUX and they offered it, don't consider RTCP candidates */
+ return TRUE;
+ }
+
candidate.foundation = foundation;
candidate.transport = transport;
diff --git a/configs/samples/queuerules.conf.sample b/configs/samples/queuerules.conf.sample
index 417f52de1..16648f98c 100644
--- a/configs/samples/queuerules.conf.sample
+++ b/configs/samples/queuerules.conf.sample
@@ -9,23 +9,40 @@
; realtime_rules = yes
;
-; It is possible to change the value of the QUEUE_MAX_PENALTY and QUEUE_MIN_PENALTY
+; It is possible to change the value of the QUEUE_MAX_PENALTY, QUEUE_MIN_PENALTY and QUEUE_RAISE_PENALTY
; channel variables in mid-call by defining rules in the queue for when to do so. This can allow for
; a call to be opened to more members or potentially a different set of members.
; The advantage to changing members this way as opposed to inserting the caller into a
; different queue with more members or reinserting the caller into the same queue with a different
; QUEUE_MAX_PENALTY or QUEUE_MIN_PENALTY set is that the caller does not lose his place in the queue.
;
+; QUEUE_MAX_PENALTY, QUEUE_MIN_PENALTY and QUEUE_RAISE_PENALTY only apply to a queue call, and are only
+; modified by these rules if they are initially set in the dialplan.
+;
+; If QUEUE_MIN_PENALTY is set, agents with a lower penalty value will not be considered for the caller.
+; If QUEUE_MAX_PENALTY is set, agents with a higher penalty value will not be considered for the caller.
+; If QUEUE_RAISE_PENALTY is set, agents with a lower penalty will be treated as having a penalty = QUEUE_RAISE_PENALTY.
+;
+; QUEUE_RAISE_PENALTY example:
+; - Agent 1 has penalty 1
+; - Agent 2 has penalty 2
+; - the queue rule is set to:
+; penaltychange => 30,,,2
+;
+; Prior to the 30 second mark, Agent 1 will take priority over Agent 2 for call distribution.
+; After 30 seconds, Agent 1's priority is bumped to 2 by the penaltychange, so both agents are treated equally.
+;
; Note: There is a limitation to these rules; a caller will follow the penaltychange rules for
; the queue that were defined at the time the caller entered the queue. If an update to the rules is
; made during the caller's stay in the queue, these will not be reflected for that caller.
;
; The syntax for these rules is
-; penaltychange => <number of seconds into the call>,<absolute or relative change to QUEUE_MAX_PENALTY>[,absolute or relative change to QUEUE_MIN_PENALTY]
+; penaltychange => <number of seconds into the call>,<absolute or relative change to QUEUE_MAX_PENALTY>[,absolute or relative change to QUEUE_MIN_PENALTY][,absolute or relative change to QUEUE_RAISE_PENALTY]
;
; Example:
; [myrule]
; penaltychange => 30,+3 ; 30 seconds into the call increase the QUEUE_MAX_PENALTY by 3, no change to QUEUE_MIN_PENALTY
; penaltychange => 60,10,5 ; 60 seconds into the call increase the QUEUE_MAX_PENALTY to 10 and increase the QUEUE_MIN_PENALTY to 5
; penaltychange => 75,,7 ; 75 seconds into the call keep the QUEUE_MAX_PENALTY the same and increase the QUEUE_MIN_PENALTY to 7
+; penaltychange => 90,,,20 ; 90 seconds into the call leave QUEUE_MAX_PENALTY and QUEUE_MIN_PENALTY untouched and set QUEUE_RAISE_PENALTY to 20
diff --git a/res/res_format_attr_h263.c b/res/res_format_attr_h263.c
index 139fbf17a..a32f40f7d 100644
--- a/res/res_format_attr_h263.c
+++ b/res/res_format_attr_h263.c
@@ -156,6 +156,8 @@ static struct ast_format *h263_parse_sdp_fmtp(const struct ast_format *format, c
while ((attrib = strsep(&attribs, ";"))) {
unsigned int val, val2 = 0, val3 = 0, val4 = 0;
+ attrib = ast_strip(attrib);
+
if (sscanf(attrib, "SQCIF=%30u", &val) == 1) {
attr->SQCIF = val;
} else if (sscanf(attrib, "QCIF=%30u", &val) == 1) {
diff --git a/res/res_format_attr_h264.c b/res/res_format_attr_h264.c
index 29d495bc7..fa3339543 100644
--- a/res/res_format_attr_h264.c
+++ b/res/res_format_attr_h264.c
@@ -184,6 +184,8 @@ static struct ast_format *h264_parse_sdp_fmtp(const struct ast_format *format, c
unsigned int val;
unsigned long int val2;
+ attrib = ast_strip(attrib);
+
if (sscanf(attrib, "profile-level-id=%lx", &val2) == 1) {
attr->PROFILE_IDC = ((val2 >> 16) & 0xFF);
attr->PROFILE_IOP = ((val2 >> 8) & 0xFF);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index f7e976272..18987cee9 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -2377,6 +2377,39 @@ error:
}
#endif
+static int rtcp_mux(struct ast_rtp *rtp, const unsigned char *packet)
+{
+ uint8_t version;
+ uint8_t pt;
+ uint8_t m;
+
+ if (!rtp->rtcp || rtp->rtcp->type != AST_RTP_INSTANCE_RTCP_MUX) {
+ return 0;
+ }
+
+ version = (packet[0] & 0XC0) >> 6;
+ if (version == 0) {
+ /* version 0 indicates this is a STUN packet and shouldn't
+ * be interpreted as a possible RTCP packet
+ */
+ return 0;
+ }
+
+ /* The second octet of a packet will be one of the following:
+ * For RTP: The marker bit (1 bit) and the RTP payload type (7 bits)
+ * For RTCP: The payload type (8)
+ *
+ * RTP has a forbidden range of payload types (64-95) since these
+ * will conflict with RTCP payload numbers if the marker bit is set.
+ */
+ m = packet[1] & 0x80;
+ pt = packet[1] & 0x7F;
+ if (m && pt >= 64 && pt <= 95) {
+ return 1;
+ }
+ return 0;
+}
+
/*! \pre instance is locked */
static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp)
{
@@ -2499,7 +2532,8 @@ static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t s
}
#endif
- if ((*in & 0xC0) && res_srtp && srtp && res_srtp->unprotect(srtp, buf, &len, rtcp) < 0) {
+ if ((*in & 0xC0) && res_srtp && srtp && res_srtp->unprotect(
+ srtp, buf, &len, rtcp || rtcp_mux(rtp, buf)) < 0) {
return -1;
}
@@ -4885,39 +4919,6 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance,
return 0;
}
-static int rtcp_mux(struct ast_rtp *rtp, const unsigned char *packet)
-{
- uint8_t version;
- uint8_t pt;
- uint8_t m;
-
- if (!rtp->rtcp || rtp->rtcp->type != AST_RTP_INSTANCE_RTCP_MUX) {
- return 0;
- }
-
- version = (packet[0] & 0XC0) >> 6;
- if (version == 0) {
- /* version 0 indicates this is a STUN packet and shouldn't
- * be interpreted as a possible RTCP packet
- */
- return 0;
- }
-
- /* The second octet of a packet will be one of the following:
- * For RTP: The marker bit (1 bit) and the RTP payload type (7 bits)
- * For RTCP: The payload type (8)
- *
- * RTP has a forbidden range of payload types (64-95) since these
- * will conflict with RTCP payload numbers if the marker bit is set.
- */
- m = packet[1] & 0x80;
- pt = packet[1] & 0x7F;
- if (m && pt >= 64 && pt <= 95) {
- return 1;
- }
- return 0;
-}
-
/*! \pre instance is locked */
static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp)
{