📄 chan_agent.c
字号:
p->dead = 0; return p;}/*! * Deletes an agent after doing some clean up. * Further documentation: How safe is this function ? What state should the agent be to be cleaned. * \param p Agent to be deleted. * \returns Always 0. */static int agent_cleanup(struct agent_pvt *p){ struct ast_channel *chan = p->owner; p->owner = NULL; chan->tech_pvt = NULL; p->app_sleep_cond = 1; /* Release ownership of the agent to other threads (presumably running the login app). */ ast_mutex_unlock(&p->app_lock); if (chan) ast_channel_free(chan); if (p->dead) { ast_mutex_destroy(&p->lock); ast_mutex_destroy(&p->app_lock); free(p); } return 0;}static int check_availability(struct agent_pvt *newlyavailable, int needlock);static int agent_answer(struct ast_channel *ast){ ast_log(LOG_WARNING, "Huh? Agent is being asked to answer?\n"); return -1;}static int __agent_start_monitoring(struct ast_channel *ast, struct agent_pvt *p, int needlock){ char tmp[AST_MAX_BUF],tmp2[AST_MAX_BUF], *pointer; char filename[AST_MAX_BUF]; int res = -1; if (!p) return -1; if (!ast->monitor) { snprintf(filename, sizeof(filename), "agent-%s-%s",p->agent, ast->uniqueid); /* substitute . for - */ if ((pointer = strchr(filename, '.'))) *pointer = '-'; snprintf(tmp, sizeof(tmp), "%s%s", savecallsin, filename); ast_monitor_start(ast, recordformat, tmp, needlock); ast_monitor_setjoinfiles(ast, 1); snprintf(tmp2, sizeof(tmp2), "%s%s.%s", urlprefix, filename, recordformatext);#if 0 ast_verbose("name is %s, link is %s\n",tmp, tmp2);#endif if (!ast->cdr) ast->cdr = ast_cdr_alloc(); ast_cdr_setuserfield(ast, tmp2); res = 0; } else ast_log(LOG_ERROR, "Recording already started on that call.\n"); return res;}static int agent_start_monitoring(struct ast_channel *ast, int needlock){ return __agent_start_monitoring(ast, ast->tech_pvt, needlock);}static struct ast_frame *agent_read(struct ast_channel *ast){ struct agent_pvt *p = ast->tech_pvt; struct ast_frame *f = NULL; static struct ast_frame answer_frame = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER }; const char *status; ast_mutex_lock(&p->lock); CHECK_FORMATS(ast, p); if (p->chan) { ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION); p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno; f = ast_read(p->chan); } else f = &ast_null_frame; if (!f) { /* If there's a channel, hang it up (if it's on a callback) make it NULL */ if (p->chan) { p->chan->_bridge = NULL; /* Note that we don't hangup if it's not a callback because Asterisk will do it for us when the PBX instance that called login finishes */ if (!ast_strlen_zero(p->loginchan)) { if (p->chan) ast_log(LOG_DEBUG, "Bridge on '%s' being cleared (2)\n", p->chan->name); if (p->owner->_state != AST_STATE_UP) { int howlong = time(NULL) - p->start; if (p->autologoff && howlong > p->autologoff) { long logintime = time(NULL) - p->loginstart; p->loginstart = 0; ast_log(LOG_NOTICE, "Agent '%s' didn't answer/confirm within %d seconds (waited %d)\n", p->name, p->autologoff, howlong); agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Autologoff"); if (persistent_agents) dump_agents(); } } status = pbx_builtin_getvar_helper(p->chan, "CHANLOCALSTATUS"); if (autologoffunavail && status && !strcasecmp(status, "CHANUNAVAIL")) { long logintime = time(NULL) - p->loginstart; p->loginstart = 0; ast_log(LOG_NOTICE, "Agent read: '%s' is not available now, auto logoff\n", p->name); agent_logoff_maintenance(p, p->loginchan, logintime, ast->uniqueid, "Chanunavail"); } ast_hangup(p->chan); if (p->wrapuptime && p->acknowledged) p->lastdisc = ast_tvadd(ast_tvnow(), ast_samp2tv(p->wrapuptime, 1000)); } p->chan = NULL; p->inherited_devicestate = -1; ast_device_state_changed("Agent/%s", p->agent); p->acknowledged = 0; } } else { /* if acknowledgement is not required, and the channel is up, we may have missed an AST_CONTROL_ANSWER (if there was one), so mark the call acknowledged anyway */ if (!p->ackcall && !p->acknowledged && p->chan && (p->chan->_state == AST_STATE_UP)) p->acknowledged = 1; switch (f->frametype) { case AST_FRAME_CONTROL: if (f->subclass == AST_CONTROL_ANSWER) { if (p->ackcall) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "%s answered, waiting for '#' to acknowledge\n", p->chan->name); /* Don't pass answer along */ ast_frfree(f); f = &ast_null_frame; } else { p->acknowledged = 1; /* Use the builtin answer frame for the recording start check below. */ ast_frfree(f); f = &answer_frame; } } break; case AST_FRAME_DTMF_BEGIN: /*ignore DTMF begin's as it can cause issues with queue announce files*/ if((!p->acknowledged && f->subclass == '#') || (f->subclass == '*' && endcall)){ ast_frfree(f); f = &ast_null_frame; } break; case AST_FRAME_DTMF_END: if (!p->acknowledged && (f->subclass == '#')) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "%s acknowledged\n", p->chan->name); p->acknowledged = 1; ast_frfree(f); f = &answer_frame; } else if (f->subclass == '*' && endcall) { /* terminates call */ ast_frfree(f); f = NULL; } break; case AST_FRAME_VOICE: case AST_FRAME_VIDEO: /* don't pass voice or video until the call is acknowledged */ if (!p->acknowledged) { ast_frfree(f); f = &ast_null_frame; } default: /* pass everything else on through */ break; } } CLEANUP(ast,p); if (p->chan && !p->chan->_bridge) { if (strcasecmp(p->chan->tech->type, "Local")) { p->chan->_bridge = ast; if (p->chan) ast_log(LOG_DEBUG, "Bridge on '%s' being set to '%s' (3)\n", p->chan->name, p->chan->_bridge->name); } } ast_mutex_unlock(&p->lock); if (recordagentcalls && f == &answer_frame) agent_start_monitoring(ast,0); return f;}static int agent_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen){ struct agent_pvt *p = ast->tech_pvt; int res = -1; ast_mutex_lock(&p->lock); if (p->chan) res = ast_channel_sendhtml(p->chan, subclass, data, datalen); ast_mutex_unlock(&p->lock); return res;}static int agent_sendtext(struct ast_channel *ast, const char *text){ struct agent_pvt *p = ast->tech_pvt; int res = -1; ast_mutex_lock(&p->lock); if (p->chan) res = ast_sendtext(p->chan, text); ast_mutex_unlock(&p->lock); return res;}static int agent_write(struct ast_channel *ast, struct ast_frame *f){ struct agent_pvt *p = ast->tech_pvt; int res = -1; CHECK_FORMATS(ast, p); ast_mutex_lock(&p->lock); if (!p->chan) res = 0; else { if ((f->frametype != AST_FRAME_VOICE) || (f->frametype != AST_FRAME_VIDEO) || (f->subclass == p->chan->writeformat)) { res = ast_write(p->chan, f); } else { ast_log(LOG_DEBUG, "Dropping one incompatible %s frame on '%s' to '%s'\n", f->frametype == AST_FRAME_VOICE ? "audio" : "video", ast->name, p->chan->name); res = 0; } } CLEANUP(ast, p); ast_mutex_unlock(&p->lock); return res;}static int agent_fixup(struct ast_channel *oldchan, struct ast_channel *newchan){ struct agent_pvt *p = newchan->tech_pvt; ast_mutex_lock(&p->lock); if (p->owner != oldchan) { ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner); ast_mutex_unlock(&p->lock); return -1; } p->owner = newchan; ast_mutex_unlock(&p->lock); return 0;}static int agent_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen){ struct agent_pvt *p = ast->tech_pvt; int res = -1; ast_mutex_lock(&p->lock); if (p->chan && !ast_check_hangup(p->chan)) res = p->chan->tech->indicate ? p->chan->tech->indicate(p->chan, condition, data, datalen) : -1; else res = 0; ast_mutex_unlock(&p->lock); return res;}static int agent_digit_begin(struct ast_channel *ast, char digit){ struct agent_pvt *p = ast->tech_pvt; ast_mutex_lock(&p->lock); if (p->chan) { ast_senddigit_begin(p->chan, digit); } ast_mutex_unlock(&p->lock); return 0;}static int agent_digit_end(struct ast_channel *ast, char digit, unsigned int duration){ struct agent_pvt *p = ast->tech_pvt; ast_mutex_lock(&p->lock); if (p->chan) { ast_senddigit_end(p->chan, digit, duration); } ast_mutex_unlock(&p->lock); return 0;}static int agent_call(struct ast_channel *ast, char *dest, int timeout){ struct agent_pvt *p = ast->tech_pvt; int res = -1; int newstate=0; ast_mutex_lock(&p->lock); p->acknowledged = 0; if (!p->chan) { if (p->pending) { ast_log(LOG_DEBUG, "Pretending to dial on pending agent\n"); newstate = AST_STATE_DIALING; res = 0; } else { ast_log(LOG_NOTICE, "Whoa, they hung up between alloc and call... what are the odds of that?\n"); res = -1; } ast_mutex_unlock(&p->lock); if (newstate) ast_setstate(ast, newstate); return res; } else if (!ast_strlen_zero(p->loginchan)) { time(&p->start); /* Call on this agent */ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "outgoing agentcall, to agent '%s', on '%s'\n", p->agent, p->chan->name); ast_set_callerid(p->chan, ast->cid.cid_num, ast->cid.cid_name, NULL); ast_channel_inherit_variables(ast, p->chan); res = ast_call(p->chan, p->loginchan, 0); CLEANUP(ast,p); ast_mutex_unlock(&p->lock); return res; } ast_verbose( VERBOSE_PREFIX_3 "agent_call, call to agent '%s' call on '%s'\n", p->agent, p->chan->name); if (option_debug > 2) ast_log(LOG_DEBUG, "Playing beep, lang '%s'\n", p->chan->language); res = ast_streamfile(p->chan, beep, p->chan->language); if (option_debug > 2) ast_log(LOG_DEBUG, "Played beep, result '%d'\n", res); if (!res) { res = ast_waitstream(p->chan, ""); if (option_debug > 2) ast_log(LOG_DEBUG, "Waited for stream, result '%d'\n", res); } if (!res) { res = ast_set_read_format(p->chan, ast_best_codec(p->chan->nativeformats)); if (option_debug > 2) ast_log(LOG_DEBUG, "Set read format, result '%d'\n", res); if (res) ast_log(LOG_WARNING, "Unable to set read format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats))); } else { /* Agent hung-up */ p->chan = NULL; p->inherited_devicestate = -1; ast_device_state_changed("Agent/%s", p->agent); } if (!res) { res = ast_set_write_format(p->chan, ast_best_codec(p->chan->nativeformats)); if (option_debug > 2) ast_log(LOG_DEBUG, "Set write format, result '%d'\n", res); if (res) ast_log(LOG_WARNING, "Unable to set write format to %s\n", ast_getformatname(ast_best_codec(p->chan->nativeformats))); } if(!res) { /* Call is immediately up, or might need ack */ if (p->ackcall > 1) newstate = AST_STATE_RINGING; else { newstate = AST_STATE_UP; if (recordagentcalls) agent_start_monitoring(ast, 0); p->acknowledged = 1; } res = 0; } CLEANUP(ast, p); ast_mutex_unlock(&p->lock); if (newstate) ast_setstate(ast, newstate); return res;}/*! \brief store/clear the global variable that stores agentid based on the callerid */static void set_agentbycallerid(const char *callerid, const char *agent){ char buf[AST_MAX_BUF]; /* if there is no Caller ID, nothing to do */ if (ast_strlen_zero(callerid)) return; snprintf(buf, sizeof(buf), "%s_%s", GETAGENTBYCALLERID, callerid); pbx_builtin_setvar_helper(NULL, buf, agent);}/*! \brief return the channel or base channel if one exists. This function assumes the channel it is called on is already locked */struct ast_channel* agent_get_base_channel(struct ast_channel *chan){ struct agent_pvt *p = NULL; struct ast_channel *base = chan; /* chan is locked by the calling function */ if (!chan || !chan->tech_pvt) { ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) with a tech_pvt (0x%ld) to get a base channel.\n", (long)chan, (chan)?(long)chan->tech_pvt:(long)NULL); return NULL; } p = chan->tech_pvt; if (p->chan) base = p->chan; return base;}int agent_set_base_channel(struct ast_channel *chan, struct ast_channel *base){ struct agent_pvt *p = NULL; if (!chan || !base) { ast_log(LOG_ERROR, "whoa, you need a channel (0x%ld) and a base channel (0x%ld) for setting.\n", (long)chan, (long)base); return -1; } p = chan->tech_pvt; if (!p) { ast_log(LOG_ERROR, "whoa, channel %s is missing his tech_pvt structure!!.\n", chan->name); return -1; } p->chan = base; return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -