Merge branch 'master' of 192.168.0.100:/repos/git/asterisk
This commit is contained in:
commit
916e420bf0
43
CHANGES
43
CHANGES
|
@ -41,6 +41,9 @@ Asterisk Manager Interface
|
|||
Description field that is set by 'description' in the channel configuration
|
||||
file.
|
||||
* Added Uniqueid header to UserEvent.
|
||||
* Added new action FilterAdd to control event filters for the current session.
|
||||
This requires the system permission and uses the same filter syntax as
|
||||
filters that can be defined in manager.conf
|
||||
|
||||
Asterisk HTTP Server
|
||||
--------------------------
|
||||
|
@ -82,6 +85,8 @@ ConfBridge
|
|||
* CONFBRIDGE_INFO dialplan function capable of retreiving information
|
||||
about a conference such as locked status and number of parties, admins,
|
||||
and marked users.
|
||||
* Addition of video_mode option in confbridge.conf for adding video support
|
||||
into a bridge profile.
|
||||
|
||||
Dialplan Variables
|
||||
------------------
|
||||
|
@ -150,6 +155,39 @@ pbx_lua
|
|||
stopped and restarted using the autoservice_stop() and autoservice_start()
|
||||
functions.
|
||||
|
||||
res_fax
|
||||
--------------------------
|
||||
* The ReceiveFAXStatus and SendFAXStatus manager events have been consolidated
|
||||
into a FAXStatus event with an 'Operation' header that will be either
|
||||
'send', 'receive', and 'gateway'.
|
||||
* T.38 gateway functionality has been added to res_fax (and res_fax_spandsp).
|
||||
Set FAXOPT(gateway)=yes to enable this functionality on a channel. This
|
||||
feature will handle converting a fax call between an audio T.30 fax terminal
|
||||
and an IFP T.38 fax terminal.
|
||||
|
||||
SIP Changes
|
||||
-----------
|
||||
* Add T38 support for REJECTED state where T.38 Negotiation is explicitly rejected.
|
||||
|
||||
Queue changes
|
||||
-------------
|
||||
* Added general option negative_penalty_invalid default off. when set
|
||||
members are seen as invalid/logged out when there penalty is negative.
|
||||
for realtime members when set remove from queue will set penalty to -1.
|
||||
* Added queue option autopausedelay when autopause is enabled it will be
|
||||
delayed for this number of seconds since last successful call if there
|
||||
was no prior call the agent will be autopaused immediately.
|
||||
* Added member option ignorebusy this when set and ringinuse is not
|
||||
will allow per member control of multiple calls as ringinuse does for
|
||||
the Queue.
|
||||
|
||||
Applications
|
||||
------------
|
||||
* Added 'v' option to MeetMe to play voicemail greetings when a user joins/leaves
|
||||
a MeetMe conference
|
||||
* Added ability to include '@parkinglot' to ParkedCall extension in order to specify
|
||||
a specific parkinglot on which to search the extension.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 1.6.2 to Asterisk 1.8 ----------------
|
||||
------------------------------------------------------------------------------
|
||||
|
@ -219,7 +257,6 @@ SIP Changes
|
|||
res_stun_monitor module support in chan_sip.
|
||||
* Addition of the 'auth_options_requests' option for turning on and off
|
||||
authentication for OPTIONS requests in chan_sip.
|
||||
* Add T38 support for REJECTED state where T.38 Negotiation is explicitly rejected.
|
||||
|
||||
|
||||
IAX2 Changes
|
||||
|
@ -324,10 +361,6 @@ Applications
|
|||
notices a change.
|
||||
* Voicemail now includes rdnis within msgXXXX.txt file.
|
||||
* Added 'D' command to ExternalIVR full details in doc/externalivr.txt
|
||||
* Added 'v' option to MeetMe to play voicemail greetings when a user joins/leaves
|
||||
a MeetMe conference
|
||||
* Added ability to include '@parkinglot' to ParkedCall extension in order to specify
|
||||
a specific parkinglot on which to search the extension.
|
||||
|
||||
Dialplan Functions
|
||||
------------------
|
||||
|
|
|
@ -50,5 +50,9 @@ pbx_lua:
|
|||
- the autoservice now defaults to being on by default
|
||||
- autoservice_start() and autoservice_start() no longer return a value.
|
||||
|
||||
Queue:
|
||||
- Mark QUEUE_MEMBER_PENALTY Deprecated it never worked for realtime members
|
||||
- QUEUE_MEMBER is now R/W supporting setting paused, ignorebusy and penalty.
|
||||
|
||||
===========================================================
|
||||
===========================================================
|
||||
|
|
|
@ -77,6 +77,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
<description>
|
||||
<para>Enters the user into a specified conference bridge. The user can exit the conference by hangup or DTMF menu option.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ConfBridge</ref>
|
||||
<ref type="function">CONFBRIDGE</ref>
|
||||
<ref type="function">CONFBRIDGE_INFO</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<function name="CONFBRIDGE" language="en_US">
|
||||
<synopsis>
|
||||
|
@ -233,6 +238,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
<description>
|
||||
</description>
|
||||
</manager>
|
||||
<manager name="ConfbridgeSetSingleVideoSrc" language="en_US">
|
||||
<synopsis>
|
||||
Set a conference user as the single video source distributed to all other participants.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<parameter name="Conference" required="true" />
|
||||
<parameter name="Channel" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
</description>
|
||||
</manager>
|
||||
|
||||
***/
|
||||
|
||||
/*!
|
||||
|
@ -547,9 +565,9 @@ static void send_leave_event(struct ast_channel *chan, const char *conf_name)
|
|||
* \param (OPTIONAL) conference_bridge_user Caller
|
||||
*
|
||||
* \note if caller is NULL, the announcment will be sent to all participants in the conference.
|
||||
* \return Returns nothing
|
||||
* \return Returns 0 on success, -1 if the user hung up
|
||||
*/
|
||||
static void announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
|
||||
static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
|
||||
{
|
||||
const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference_bridge->b_profile.sounds);
|
||||
const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference_bridge->b_profile.sounds);
|
||||
|
@ -557,14 +575,14 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
|
|||
|
||||
if (conference_bridge->users == 1) {
|
||||
/* Awww we are the only person in the conference bridge */
|
||||
return;
|
||||
return 0;
|
||||
} else if (conference_bridge->users == 2) {
|
||||
if (conference_bridge_user) {
|
||||
/* Eep, there is one other person */
|
||||
if (ast_stream_and_wait(conference_bridge_user->chan,
|
||||
only_one,
|
||||
"")) {
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
play_sound_file(conference_bridge, only_one);
|
||||
|
@ -575,15 +593,15 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
|
|||
if (ast_stream_and_wait(conference_bridge_user->chan,
|
||||
there_are,
|
||||
"")) {
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
if (ast_stream_and_wait(conference_bridge_user->chan,
|
||||
other_in_party,
|
||||
"")) {
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
play_sound_file(conference_bridge, there_are);
|
||||
|
@ -591,6 +609,7 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
|
|||
play_sound_file(conference_bridge, other_in_party);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -600,15 +619,79 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
|
|||
* \param chan Channel to play audio prompt to
|
||||
* \param file Prompt to play
|
||||
*
|
||||
* \return Returns nothing
|
||||
* \return Returns 0 on success, -1 if the user hung up
|
||||
*
|
||||
* \note This function assumes that conference_bridge is locked
|
||||
*/
|
||||
static void play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
|
||||
static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
|
||||
{
|
||||
int res;
|
||||
ao2_unlock(conference_bridge);
|
||||
ast_stream_and_wait(chan, file, "");
|
||||
res = ast_stream_and_wait(chan, file, "");
|
||||
ao2_lock(conference_bridge);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void handle_video_on_join(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
|
||||
{
|
||||
/* only automatically set video source for marked users */
|
||||
if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
|
||||
int set = 1;
|
||||
struct conference_bridge_user *tmp_user = NULL;
|
||||
ao2_lock(conference_bridge);
|
||||
/* see if anyone is already the video src */
|
||||
AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
|
||||
if (tmp_user == conference_bridge_user) {
|
||||
continue;
|
||||
}
|
||||
if (ast_bridge_is_video_src(conference_bridge->bridge, tmp_user->chan)) {
|
||||
set = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ao2_unlock(conference_bridge);
|
||||
if (set) {
|
||||
ast_bridge_set_single_src_video_mode(conference_bridge->bridge, conference_bridge_user->chan);
|
||||
}
|
||||
} else if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
|
||||
/* we joined and are video capable, we override anyone else that may have already been the video feed */
|
||||
ast_bridge_set_single_src_video_mode(conference_bridge->bridge, conference_bridge_user->chan);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_video_on_exit(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
|
||||
{
|
||||
struct conference_bridge_user *tmp_user = NULL;
|
||||
|
||||
/* if this isn't a video source, nothing to update */
|
||||
if (!ast_bridge_is_video_src(conference_bridge->bridge, conference_bridge_user->chan)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ast_bridge_remove_video_src(conference_bridge->bridge, conference_bridge_user->chan);
|
||||
|
||||
/* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
|
||||
if (!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
|
||||
!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make the next avaliable marked user the video src. */
|
||||
ao2_lock(conference_bridge);
|
||||
AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
|
||||
if (tmp_user == conference_bridge_user) {
|
||||
continue;
|
||||
}
|
||||
if (ast_test_flag(&tmp_user->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
ast_bridge_set_single_src_video_mode(conference_bridge->bridge, tmp_user->chan);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ao2_unlock(conference_bridge);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -617,16 +700,17 @@ static void play_prompt_to_channel(struct conference_bridge *conference_bridge,
|
|||
* \param conference_bridge Conference bridge being joined
|
||||
* \param conference_bridge_user Conference bridge user joining
|
||||
*
|
||||
* \return Returns nothing
|
||||
* \return Returns 0 on success, -1 if the user hung up
|
||||
*/
|
||||
static void post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
|
||||
static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
|
||||
{
|
||||
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
|
||||
struct conference_bridge_user *other_conference_bridge_user = NULL;
|
||||
|
||||
/* If we are not the first marked user to join just bail out now */
|
||||
/* If we are not the first user to join, then the users are already
|
||||
* in the conference so we do not need to update them. */
|
||||
if (conference_bridge->markedusers >= 2) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Iterate through every participant stopping MOH on them if need be */
|
||||
|
@ -661,19 +745,21 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct
|
|||
other_conference_bridge_user->features.mute = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
/* If a marked user already exists in the conference bridge we can just bail out now */
|
||||
if (conference_bridge->markedusers) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
/* Be sure we are muted so we can't talk to anybody else waiting */
|
||||
conference_bridge_user->features.mute = 1;
|
||||
/* If we have not been quieted play back that they are waiting for the leader */
|
||||
if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
|
||||
play_prompt_to_channel(conference_bridge,
|
||||
if (play_prompt_to_channel(conference_bridge,
|
||||
conference_bridge_user->chan,
|
||||
conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, conference_bridge_user->b_profile.sounds));
|
||||
conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, conference_bridge_user->b_profile.sounds))) {
|
||||
/* user hungup while the sound was playing */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* Start music on hold if needed */
|
||||
/* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
|
||||
|
@ -684,6 +770,7 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct
|
|||
conference_bridge_user->playing_moh = 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -692,17 +779,20 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct
|
|||
* \param conference_bridge Conference bridge being joined
|
||||
* \param conference_bridge_user Conference bridge user joining
|
||||
*
|
||||
* \return Returns nothing
|
||||
* \return Returns 0 on success, -1 if the user hung up
|
||||
*/
|
||||
static void post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
|
||||
static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
|
||||
{
|
||||
/* Play back audio prompt and start MOH if need be if we are the first participant */
|
||||
if (conference_bridge->users == 1) {
|
||||
/* If audio prompts have not been quieted or this prompt quieted play it on out */
|
||||
if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
|
||||
play_prompt_to_channel(conference_bridge,
|
||||
if (play_prompt_to_channel(conference_bridge,
|
||||
conference_bridge_user->chan,
|
||||
conf_get_sound(CONF_SOUND_ONLY_PERSON, conference_bridge_user->b_profile.sounds));
|
||||
conf_get_sound(CONF_SOUND_ONLY_PERSON, conference_bridge_user->b_profile.sounds))) {
|
||||
/* user hungup while the sound was playing */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* If we need to start music on hold on the channel do so now */
|
||||
/* We need to re-check the number of users in the conference bridge here because another conference bridge
|
||||
|
@ -712,13 +802,16 @@ static void post_join_unmarked(struct conference_bridge *conference_bridge, stru
|
|||
ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL);
|
||||
conference_bridge_user->playing_moh = 1;
|
||||
}
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Announce number of users if need be */
|
||||
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
|
||||
ao2_unlock(conference_bridge);
|
||||
announce_user_count(conference_bridge, conference_bridge_user);
|
||||
if (announce_user_count(conference_bridge, conference_bridge_user)) {
|
||||
ao2_lock(conference_bridge);
|
||||
return -1;
|
||||
}
|
||||
ao2_lock(conference_bridge);
|
||||
}
|
||||
|
||||
|
@ -737,9 +830,13 @@ static void post_join_unmarked(struct conference_bridge *conference_bridge, stru
|
|||
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
|
||||
(conference_bridge->users > conference_bridge_user->u_profile.announce_user_count_all_after)) {
|
||||
ao2_unlock(conference_bridge);
|
||||
announce_user_count(conference_bridge, NULL);
|
||||
if (announce_user_count(conference_bridge, NULL)) {
|
||||
ao2_lock(conference_bridge);
|
||||
return -1;
|
||||
}
|
||||
ao2_lock(conference_bridge);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -772,6 +869,8 @@ static void destroy_conference_bridge(void *obj)
|
|||
conf_bridge_profile_destroy(&conference_bridge->b_profile);
|
||||
}
|
||||
|
||||
static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
|
||||
|
||||
/*!
|
||||
* \brief Join a conference bridge
|
||||
*
|
||||
|
@ -840,6 +939,10 @@ static struct conference_bridge *join_conference_bridge(const char *name, struct
|
|||
/* Set the internal mixing interval on the bridge from the bridge profile */
|
||||
ast_bridge_set_mixing_interval(conference_bridge->bridge, conference_bridge->b_profile.mix_interval);
|
||||
|
||||
if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
|
||||
ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
|
||||
}
|
||||
|
||||
/* Setup lock for playback channel */
|
||||
ast_mutex_init(&conference_bridge->playback_lock);
|
||||
|
||||
|
@ -876,9 +979,17 @@ static struct conference_bridge *join_conference_bridge(const char *name, struct
|
|||
|
||||
/* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
|
||||
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER | USER_OPT_WAITMARKED)) {
|
||||
post_join_marked(conference_bridge, conference_bridge_user);
|
||||
if (post_join_marked(conference_bridge, conference_bridge_user)) {
|
||||
ao2_unlock(conference_bridge);
|
||||
leave_conference_bridge(conference_bridge, conference_bridge_user);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
post_join_unmarked(conference_bridge, conference_bridge_user);
|
||||
if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
|
||||
ao2_unlock(conference_bridge);
|
||||
leave_conference_bridge(conference_bridge, conference_bridge_user);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* check to see if recording needs to be started or not */
|
||||
|
@ -1335,14 +1446,16 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
|
|||
ast_moh_stop(chan);
|
||||
}
|
||||
ast_stream_and_wait(chan, join_sound, "");
|
||||
if (conference_bridge_user.playing_moh) {
|
||||
ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL);
|
||||
}
|
||||
ast_autoservice_start(chan);
|
||||
play_sound_file(conference_bridge, join_sound);
|
||||
ast_autoservice_stop(chan);
|
||||
if (conference_bridge_user.playing_moh) {
|
||||
ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
handle_video_on_join(conference_bridge, &conference_bridge_user);
|
||||
|
||||
/* Join our conference bridge for real */
|
||||
send_join_event(conference_bridge_user.chan, conference_bridge->name);
|
||||
ast_bridge_join(conference_bridge->bridge,
|
||||
|
@ -1352,6 +1465,9 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
|
|||
&conference_bridge_user.tech_args);
|
||||
send_leave_event(conference_bridge_user.chan, conference_bridge->name);
|
||||
|
||||
|
||||
handle_video_on_exit(conference_bridge, &conference_bridge_user);
|
||||
|
||||
/* if this user has a intro, play it when leaving */
|
||||
if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
|
||||
ast_autoservice_start(chan);
|
||||
|
@ -1373,9 +1489,6 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
|
|||
leave_conference_bridge(conference_bridge, &conference_bridge_user);
|
||||
conference_bridge = NULL;
|
||||
|
||||
/* Can't forget to clean up the features structure, or else we risk a memory leak */
|
||||
ast_bridge_features_cleanup(&conference_bridge_user.features);
|
||||
|
||||
/* If the user was kicked from the conference play back the audio prompt for it */
|
||||
if (!quiet && conference_bridge_user.kicked) {
|
||||
res = ast_stream_and_wait(chan,
|
||||
|
@ -1657,6 +1770,11 @@ static int execute_menu_entry(struct conference_bridge *conference_bridge,
|
|||
break;
|
||||
case MENU_ACTION_NOOP:
|
||||
break;
|
||||
case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
|
||||
ao2_lock(conference_bridge);
|
||||
ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
|
||||
ao2_unlock(conference_bridge);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
@ -2412,6 +2530,55 @@ static int action_confbridgestoprecord(struct mansession *s, const struct messag
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *conference = astman_get_header(m, "Conference");
|
||||
const char *channel = astman_get_header(m, "Channel");
|
||||
struct conference_bridge_user *participant = NULL;
|
||||
struct conference_bridge *bridge = NULL;
|
||||
struct conference_bridge tmp;
|
||||
|
||||
if (ast_strlen_zero(conference)) {
|
||||
astman_send_error(s, m, "No Conference name provided.");
|
||||
return 0;
|
||||
}
|
||||
if (ast_strlen_zero(channel)) {
|
||||
astman_send_error(s, m, "No channel name provided.");
|
||||
return 0;
|
||||
}
|
||||
if (!ao2_container_count(conference_bridges)) {
|
||||
astman_send_error(s, m, "No active conferences.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_copy_string(tmp.name, conference, sizeof(tmp.name));
|
||||
bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
|
||||
if (!bridge) {
|
||||
astman_send_error(s, m, "No Conference by that name found.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* find channel and set as video src. */
|
||||
ao2_lock(bridge);
|
||||
AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
|
||||
if (!strncmp(channel, participant->chan->name, strlen(channel))) {
|
||||
ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ao2_unlock(bridge);
|
||||
ao2_ref(bridge, -1);
|
||||
|
||||
/* do not access participant after bridge unlock. We are just
|
||||
* using this check to see if it was found or not */
|
||||
if (!participant) {
|
||||
astman_send_error(s, m, "No channel by that name found in conference.");
|
||||
return 0;
|
||||
}
|
||||
astman_send_ack(s, m, "Conference single video source set.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
char *parse = NULL;
|
||||
|
@ -2543,6 +2710,7 @@ static int load_module(void)
|
|||
res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
|
||||
res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord);
|
||||
res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord);
|
||||
res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
|
||||
|
||||
conf_load_config(0);
|
||||
return res;
|
||||
|
|
|
@ -65,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include "asterisk/aoc.h"
|
||||
#include "asterisk/ccss.h"
|
||||
#include "asterisk/indications.h"
|
||||
#include "asterisk/framehook.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="Dial" language="en_US">
|
||||
|
@ -631,7 +632,8 @@ END_OPTIONS );
|
|||
OPT_CALLER_HANGUP | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | \
|
||||
OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | \
|
||||
OPT_CALLER_PARK | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB) && \
|
||||
!chan->audiohooks && !peer->audiohooks)
|
||||
!chan->audiohooks && !peer->audiohooks && \
|
||||
ast_framehook_list_is_empty(chan->framehooks) && ast_framehook_list_is_empty(peer->framehooks))
|
||||
|
||||
/*
|
||||
* The list of active channels
|
||||
|
|
|
@ -371,7 +371,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
</syntax>
|
||||
<description>
|
||||
<para>Run admin <replaceable>command</replaceable> for a specific
|
||||
<replaceable>channel</replaceable> in any coference.</para>
|
||||
<replaceable>channel</replaceable> in any conference.</para>
|
||||
</description>
|
||||
</application>
|
||||
<application name="SLAStation" language="en_US">
|
||||
|
|
377
apps/app_queue.c
377
apps/app_queue.c
|
@ -57,7 +57,7 @@
|
|||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<use>res_monitor</use>
|
||||
<use type="module">res_monitor</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
@ -521,11 +521,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
<enum name="count">
|
||||
<para>Returns the total number of members for the specified queue.</para>
|
||||
</enum>
|
||||
<enum name="penalty">
|
||||
<para>Gets or sets queue member penalty.</para>
|
||||
</enum>
|
||||
<enum name="paused">
|
||||
<para>Gets or sets queue member paused status.</para>
|
||||
</enum>
|
||||
<enum name="ignorebusy">
|
||||
<para>Gets or sets queue member ignorebusy.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="interface" required="false" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</para>
|
||||
<para>Allows access to queue counts [R] and member information [R/W].</para>
|
||||
<para>
|
||||
<replaceable>queuename</replaceable> is required for all operations
|
||||
<replaceable>interface</replaceable> is required for all member operations.
|
||||
</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">Queue</ref>
|
||||
|
@ -658,6 +672,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
</syntax>
|
||||
<description>
|
||||
<para>Gets or sets queue members penalty.</para>
|
||||
<warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">Queue</ref>
|
||||
|
@ -680,7 +695,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
Queues.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
</syntax>
|
||||
<description>
|
||||
</description>
|
||||
|
@ -934,6 +948,9 @@ static struct ast_event_sub *device_state_sub;
|
|||
/*! \brief queues.conf [general] option */
|
||||
static int update_cdr = 0;
|
||||
|
||||
/*! \brief queues.conf [general] option */
|
||||
static int negative_penalty_invalid = 0;
|
||||
|
||||
enum queue_result {
|
||||
QUEUE_UNKNOWN = 0,
|
||||
QUEUE_TIMEOUT = 1,
|
||||
|
@ -1043,6 +1060,7 @@ struct member {
|
|||
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 */
|
||||
unsigned int ignorebusy:1; /*!< Flag to ignore member if the status is not available */
|
||||
};
|
||||
|
||||
enum empty_conditions {
|
||||
|
@ -1160,6 +1178,7 @@ struct call_queue {
|
|||
int timeout; /*!< How long to wait for an answer */
|
||||
int weight; /*!< Respective weight */
|
||||
int autopause; /*!< Auto pause queue members if they fail to answer */
|
||||
int autopausedelay; /*!< Delay auto pause for autopausedelay seconds since last call */
|
||||
int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
|
||||
|
||||
/* Queue strategy things */
|
||||
|
@ -1190,9 +1209,10 @@ static AST_LIST_HEAD_STATIC(rule_lists, rule_list);
|
|||
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 void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
|
||||
static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
|
||||
/*! \brief sets the QUEUESTATUS channel variable */
|
||||
static void set_queue_result(struct ast_channel *chan, enum queue_result res)
|
||||
{
|
||||
|
@ -1698,6 +1718,7 @@ static void init_queue(struct call_queue *q)
|
|||
q->numperiodicannounce = 0;
|
||||
q->autopause = QUEUE_AUTOPAUSE_OFF;
|
||||
q->timeoutpriority = TIMEOUT_PRIORITY_APP;
|
||||
q->autopausedelay = 0;
|
||||
if (!q->members) {
|
||||
if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
|
||||
/* linear strategy depends on order, so we have to place all members in a single bucket */
|
||||
|
@ -1760,7 +1781,7 @@ static void clear_queue(struct call_queue *q)
|
|||
* \retval 0 on success
|
||||
* \note Call this with the rule_lists locked
|
||||
*/
|
||||
static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
|
||||
static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
|
||||
{
|
||||
char *timestr, *maxstr, *minstr, *contentdup;
|
||||
struct penalty_rule *rule = NULL, *rule_iter;
|
||||
|
@ -2003,6 +2024,8 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
|
|||
q->montype = 1;
|
||||
} else if (!strcasecmp(param, "autopause")) {
|
||||
q->autopause = autopause2int(val);
|
||||
} else if (!strcasecmp(param, "autopausedelay")) {
|
||||
q->autopausedelay = atoi(val);
|
||||
} else if (!strcasecmp(param, "maxlen")) {
|
||||
q->maxlen = atoi(val);
|
||||
if (q->maxlen < 0)
|
||||
|
@ -2081,7 +2104,9 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
|
|||
int penalty = 0;
|
||||
int paused = 0;
|
||||
int found = 0;
|
||||
int ignorebusy = 0;
|
||||
|
||||
const char *config_val;
|
||||
const char *rt_uniqueid = ast_variable_retrieve(member_config, interface, "uniqueid");
|
||||
const char *membername = S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface);
|
||||
const char *state_interface = S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface);
|
||||
|
@ -2095,8 +2120,11 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
|
|||
|
||||
if (penalty_str) {
|
||||
penalty = atoi(penalty_str);
|
||||
if (penalty < 0)
|
||||
if ((penalty < 0) && negative_penalty_invalid) {
|
||||
return;
|
||||
} else if (penalty < 0) {
|
||||
penalty = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (paused_str) {
|
||||
|
@ -2105,31 +2133,39 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
|
|||
paused = 0;
|
||||
}
|
||||
|
||||
/* Find member by realtime uniqueid and update */
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((m = ao2_iterator_next(&mem_iter))) {
|
||||
if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
|
||||
m->dead = 0; /* Do not delete this one. */
|
||||
ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
|
||||
if (paused_str)
|
||||
m->paused = paused;
|
||||
if (strcasecmp(state_interface, m->state_interface)) {
|
||||
ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
|
||||
}
|
||||
m->penalty = penalty;
|
||||
found = 1;
|
||||
ao2_ref(m, -1);
|
||||
break;
|
||||
}
|
||||
ao2_ref(m, -1);
|
||||
}
|
||||
if ((config_val = ast_variable_retrieve(member_config, interface, "ignorebusy"))) {
|
||||
ignorebusy = ast_true(config_val);
|
||||
} else {
|
||||
ignorebusy = 1;
|
||||
}
|
||||
|
||||
/* Find member by realtime uniqueid and update */
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((m = ao2_iterator_next(&mem_iter))) {
|
||||
if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
|
||||
m->dead = 0; /* Do not delete this one. */
|
||||
ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
|
||||
if (paused_str)
|
||||
m->paused = paused;
|
||||
if (strcasecmp(state_interface, m->state_interface)) {
|
||||
ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
|
||||
}
|
||||
m->penalty = penalty;
|
||||
m->ignorebusy = ignorebusy;
|
||||
found = 1;
|
||||
ao2_ref(m, -1);
|
||||
break;
|
||||
}
|
||||
ao2_ref(m, -1);
|
||||
}
|
||||
ao2_iterator_destroy(&mem_iter);
|
||||
|
||||
/* Create a new member */
|
||||
if (!found) {
|
||||
/* Create a new member */
|
||||
if (!found) {
|
||||
if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
|
||||
m->dead = 0;
|
||||
m->realtime = 1;
|
||||
m->ignorebusy = ignorebusy;
|
||||
ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
|
||||
ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
|
||||
ao2_link(q->members, m);
|
||||
|
@ -2873,16 +2909,24 @@ static int num_available_members(struct call_queue *q)
|
|||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((mem = ao2_iterator_next(&mem_iter))) {
|
||||
switch (mem->status) {
|
||||
case AST_DEVICE_INUSE:
|
||||
if (!q->ringinuse)
|
||||
case AST_DEVICE_INVALID:
|
||||
case AST_DEVICE_UNAVAILABLE:
|
||||
break;
|
||||
case AST_DEVICE_INUSE:
|
||||
case AST_DEVICE_BUSY:
|
||||
case AST_DEVICE_RINGING:
|
||||
case AST_DEVICE_RINGINUSE:
|
||||
case AST_DEVICE_ONHOLD:
|
||||
if ((!q->ringinuse) || (!mem->ignorebusy)) {
|
||||
break;
|
||||
}
|
||||
/* else fall through */
|
||||
case AST_DEVICE_NOT_INUSE:
|
||||
case AST_DEVICE_UNKNOWN:
|
||||
if (!mem->paused) {
|
||||
avl++;
|
||||
}
|
||||
break;
|
||||
/* else fall through */
|
||||
case AST_DEVICE_NOT_INUSE:
|
||||
case AST_DEVICE_UNKNOWN:
|
||||
if (!mem->paused) {
|
||||
avl++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ao2_ref(mem, -1);
|
||||
|
||||
|
@ -3010,38 +3054,54 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
|
|||
char tech[256];
|
||||
char *location;
|
||||
const char *macrocontext, *macroexten;
|
||||
enum ast_device_state newstate;
|
||||
|
||||
/* on entry here, we know that tmp->chan == NULL */
|
||||
if (tmp->member->paused) {
|
||||
ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
|
||||
if (qe->chan->cdr) {
|
||||
ast_cdr_busy(qe->chan->cdr);
|
||||
}
|
||||
tmp->stillgoing = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
|
||||
(!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
|
||||
ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
|
||||
ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
|
||||
(tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
|
||||
if (qe->chan->cdr)
|
||||
if (qe->chan->cdr) {
|
||||
ast_cdr_busy(qe->chan->cdr);
|
||||
}
|
||||
tmp->stillgoing = 0;
|
||||
(*busies)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
|
||||
ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
|
||||
if (qe->chan->cdr)
|
||||
ast_cdr_busy(qe->chan->cdr);
|
||||
tmp->stillgoing = 0;
|
||||
return 0;
|
||||
if (!qe->parent->ringinuse || !tmp->member->ignorebusy) {
|
||||
if ((tmp->member->status == AST_DEVICE_UNKNOWN) || (tmp->member->status == AST_DEVICE_NOT_INUSE)) {
|
||||
newstate = ast_parse_device_state(tmp->member->interface);
|
||||
if (newstate != tmp->member->status) {
|
||||
ast_log(LOG_ERROR, "Found a channel matching iterface %s while status was %i changed to %i\n",
|
||||
tmp->member->interface, tmp->member->status, newstate);
|
||||
update_status(qe->parent, tmp->member, newstate);
|
||||
}
|
||||
}
|
||||
if ((tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
|
||||
ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
|
||||
if (qe->chan->cdr) {
|
||||
ast_cdr_busy(qe->chan->cdr);
|
||||
}
|
||||
tmp->stillgoing = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp->member->paused) {
|
||||
ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
|
||||
if (qe->chan->cdr)
|
||||
ast_cdr_busy(qe->chan->cdr);
|
||||
tmp->stillgoing = 0;
|
||||
return 0;
|
||||
}
|
||||
if (use_weight && compare_weight(qe->parent,tmp->member)) {
|
||||
ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
|
||||
if (qe->chan->cdr)
|
||||
if (qe->chan->cdr) {
|
||||
ast_cdr_busy(qe->chan->cdr);
|
||||
}
|
||||
tmp->stillgoing = 0;
|
||||
(*busies)++;
|
||||
return 0;
|
||||
|
@ -3056,8 +3116,9 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
|
|||
/* Request the peer */
|
||||
tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
|
||||
if (!tmp->chan) { /* If we can't, just go on to the next call */
|
||||
if (qe->chan->cdr)
|
||||
if (qe->chan->cdr) {
|
||||
ast_cdr_busy(qe->chan->cdr);
|
||||
}
|
||||
tmp->stillgoing = 0;
|
||||
|
||||
ao2_lock(qe->parent);
|
||||
|
@ -3396,6 +3457,18 @@ static void rna(int rnatime, struct queue_ent *qe, char *interface, char *member
|
|||
}
|
||||
ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
|
||||
if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
|
||||
if (qe->parent->autopausedelay > 0) {
|
||||
struct member *mem;
|
||||
ao2_lock(qe->parent);
|
||||
if ((mem = interface_exists(qe->parent, interface))) {
|
||||
time_t idletime = time(&idletime)-mem->lastcall;
|
||||
if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
|
||||
ao2_unlock(qe->parent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ao2_unlock(qe->parent);
|
||||
}
|
||||
if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
|
||||
if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
|
||||
ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
|
||||
|
@ -4707,8 +4780,9 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
|
|||
else
|
||||
ast_moh_stop(qe->chan);
|
||||
/* If appropriate, log that we have a destination channel */
|
||||
if (qe->chan->cdr)
|
||||
if (qe->chan->cdr) {
|
||||
ast_cdr_setdestchan(qe->chan->cdr, peer->name);
|
||||
}
|
||||
/* Make sure channels are compatible */
|
||||
res = ast_channel_make_compatible(qe->chan, peer);
|
||||
if (res < 0) {
|
||||
|
@ -4788,10 +4862,11 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
|
|||
if (mixmonapp) {
|
||||
ast_debug(1, "Starting MixMonitor as requested.\n");
|
||||
if (!monitorfilename) {
|
||||
if (qe->chan->cdr)
|
||||
if (qe->chan->cdr) {
|
||||
ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
|
||||
else
|
||||
} else {
|
||||
snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
|
||||
}
|
||||
} else {
|
||||
const char *m = monitorfilename;
|
||||
for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
|
||||
|
@ -4858,12 +4933,13 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
|
|||
|
||||
ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
|
||||
/* We purposely lock the CDR so that pbx_exec does not update the application data */
|
||||
if (qe->chan->cdr)
|
||||
if (qe->chan->cdr) {
|
||||
ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
|
||||
}
|
||||
pbx_exec(qe->chan, mixmonapp, mixmonargs);
|
||||
if (qe->chan->cdr)
|
||||
if (qe->chan->cdr) {
|
||||
ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
|
||||
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
|
||||
}
|
||||
|
@ -5180,7 +5256,10 @@ static int remove_from_queue(const char *queuename, const char *interface)
|
|||
ao2_lock(q);
|
||||
if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
|
||||
/* XXX future changes should beware of this assumption!! */
|
||||
if (!mem->dynamic) {
|
||||
/*Change Penalty on realtime users*/
|
||||
if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid) && negative_penalty_invalid) {
|
||||
update_realtime_member_field(mem, q->name, "penalty", "-1");
|
||||
} else if (!mem->dynamic) {
|
||||
ao2_ref(mem, -1);
|
||||
ao2_unlock(q);
|
||||
queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
|
||||
|
@ -5355,35 +5434,34 @@ static int set_member_penalty(const char *queuename, const char *interface, int
|
|||
int foundinterface = 0, foundqueue = 0;
|
||||
struct call_queue *q;
|
||||
struct member *mem;
|
||||
struct ao2_iterator queue_iter;
|
||||
char rtpenalty[80];
|
||||
|
||||
if (penalty < 0) {
|
||||
if (penalty < 0 && !negative_penalty_invalid) {
|
||||
ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
|
||||
return RESULT_FAILURE;
|
||||
}
|
||||
|
||||
queue_iter = ao2_iterator_init(queues, 0);
|
||||
while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
|
||||
if ((q = load_realtime_queue(queuename))) {
|
||||
foundqueue++;
|
||||
ao2_lock(q);
|
||||
if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
|
||||
foundqueue++;
|
||||
if ((mem = interface_exists(q, interface))) {
|
||||
foundinterface++;
|
||||
if ((mem = interface_exists(q, interface))) {
|
||||
foundinterface++;
|
||||
if (!mem->realtime) {
|
||||
mem->penalty = penalty;
|
||||
|
||||
ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
|
||||
manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
|
||||
"Queue: %s\r\n"
|
||||
"Location: %s\r\n"
|
||||
"Penalty: %d\r\n",
|
||||
q->name, mem->interface, penalty);
|
||||
ao2_ref(mem, -1);
|
||||
} else {
|
||||
sprintf(rtpenalty,"%i", penalty);
|
||||
update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
|
||||
}
|
||||
ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
|
||||
manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
|
||||
"Queue: %s\r\n"
|
||||
"Location: %s\r\n"
|
||||
"Penalty: %d\r\n",
|
||||
q->name, mem->interface, penalty);
|
||||
ao2_ref(mem, -1);
|
||||
}
|
||||
ao2_unlock(q);
|
||||
queue_t_unref(q, "Done with iterator");
|
||||
}
|
||||
ao2_iterator_destroy(&queue_iter);
|
||||
|
||||
if (foundinterface) {
|
||||
return RESULT_SUCCESS;
|
||||
|
@ -5769,7 +5847,6 @@ static void copy_rules(struct queue_ent *qe, const char *rulename)
|
|||
struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
|
||||
if (!new_pr) {
|
||||
ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
|
||||
AST_LIST_UNLOCK(&rule_lists);
|
||||
break;
|
||||
}
|
||||
new_pr->time = pr_iter->time;
|
||||
|
@ -6157,31 +6234,37 @@ static int queue_function_exists(struct ast_channel *chan, const char *cmd, char
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
/*!
|
||||
* \brief Get number either busy / free / ready or total members of a specific queue
|
||||
* \retval number of members (busy / free / ready / total)
|
||||
* \brief Get or set member properties penalty / paused / ignorebusy
|
||||
* \retval number of members (busy / free / ready / total) or member info (penalty / paused / ignorebusy)
|
||||
* \retval -1 on error
|
||||
*/
|
||||
static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
int count = 0;
|
||||
struct member *m;
|
||||
struct ao2_iterator mem_iter;
|
||||
struct call_queue *q;
|
||||
char *option;
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(queuename);
|
||||
AST_APP_ARG(option);
|
||||
AST_APP_ARG(interface);
|
||||
);
|
||||
/* Make sure the returned value on error is zero length string. */
|
||||
buf[0] = '\0';
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((option = strchr(data, ',')))
|
||||
*option++ = '\0';
|
||||
else
|
||||
option = "logged";
|
||||
if ((q = load_realtime_queue(data))) {
|
||||
AST_STANDARD_APP_ARGS(args, data);
|
||||
|
||||
if ((q = load_realtime_queue(args.queuename))) {
|
||||
ao2_lock(q);
|
||||
if (!strcasecmp(option, "logged")) {
|
||||
if (!strcasecmp(args.option, "logged")) {
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((m = ao2_iterator_next(&mem_iter))) {
|
||||
/* Count the agents who are logged in and presently answering calls */
|
||||
|
@ -6191,7 +6274,7 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
|
|||
ao2_ref(m, -1);
|
||||
}
|
||||
ao2_iterator_destroy(&mem_iter);
|
||||
} else if (!strcasecmp(option, "free")) {
|
||||
} else if (!strcasecmp(args.option, "free")) {
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
while ((m = ao2_iterator_next(&mem_iter))) {
|
||||
/* Count the agents who are logged in and presently answering calls */
|
||||
|
@ -6201,7 +6284,7 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
|
|||
ao2_ref(m, -1);
|
||||
}
|
||||
ao2_iterator_destroy(&mem_iter);
|
||||
} else if (!strcasecmp(option, "ready")) {
|
||||
} else if (!strcasecmp(args.option, "ready")) {
|
||||
time_t now;
|
||||
time(&now);
|
||||
mem_iter = ao2_iterator_init(q->members, 0);
|
||||
|
@ -6214,22 +6297,104 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
|
|||
ao2_ref(m, -1);
|
||||
}
|
||||
ao2_iterator_destroy(&mem_iter);
|
||||
} else /* must be "count" */
|
||||
} else if (!strcasecmp(args.option, "count") || ast_strlen_zero(args.option)) {
|
||||
count = q->membercount;
|
||||
} else if (!strcasecmp(args.option, "penalty") && !ast_strlen_zero(args.interface) &&
|
||||
((m = interface_exists(q, args.interface)))) {
|
||||
count = m->penalty;
|
||||
} else if (!strcasecmp(args.option, "paused") && !ast_strlen_zero(args.interface) &&
|
||||
((m = interface_exists(q, args.interface)))) {
|
||||
count = m->paused;
|
||||
} else if (!strcasecmp(args.option, "ignorebusy") && !ast_strlen_zero(args.interface) &&
|
||||
((m = interface_exists(q, args.interface)))) {
|
||||
count = m->ignorebusy;
|
||||
}
|
||||
ao2_unlock(q);
|
||||
queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
|
||||
} else
|
||||
ast_log(LOG_WARNING, "queue %s was not found\n", data);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
|
||||
}
|
||||
|
||||
snprintf(buf, len, "%d", count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
/*! \brief Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ignorebusy. */
|
||||
static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
|
||||
{
|
||||
int memvalue;
|
||||
struct call_queue *q;
|
||||
struct member *m;
|
||||
char rtvalue[80];
|
||||
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(queuename);
|
||||
AST_APP_ARG(option);
|
||||
AST_APP_ARG(interface);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER(<queuename>,<option>,<interface>)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, data);
|
||||
|
||||
if (args.argc < 3) {
|
||||
ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(args.interface) && ast_strlen_zero(args.option)) {
|
||||
ast_log (LOG_ERROR, "<interface> and <option> parameter's can't be null\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memvalue = atoi(value);
|
||||
|
||||
if (!strcasecmp(args.option, "penalty")) {
|
||||
/* if queuename = NULL then penalty will be set for interface in all the queues.*/
|
||||
if (set_member_penalty(args.queuename, args.interface, memvalue)) {
|
||||
ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
|
||||
return -1;
|
||||
}
|
||||
} else if ((q = load_realtime_queue(args.queuename))) {
|
||||
ao2_lock(q);
|
||||
if ((m = interface_exists(q, args.interface))) {
|
||||
sprintf(rtvalue, "%s",(memvalue <= 0) ? "0" : "1");
|
||||
if (!strcasecmp(args.option, "paused")) {
|
||||
if (m->realtime) {
|
||||
update_realtime_member_field(m, q->name, args.option, rtvalue);
|
||||
} else {
|
||||
m->paused = (memvalue <= 0) ? 0 : 1;
|
||||
}
|
||||
} else if (!strcasecmp(args.option, "ignorebusy")) {
|
||||
if (m->realtime) {
|
||||
update_realtime_member_field(m, q->name, args.option, rtvalue);
|
||||
} else {
|
||||
m->ignorebusy = (memvalue <= 0) ? 0 : 1;
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Invalid option, only penalty , paused or ignorebusy are valid\n");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Invalid interface or queue\n");
|
||||
return -1;
|
||||
}
|
||||
ao2_unlock(q);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Invalid queue\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Get the total number of members in a specific queue (Deprecated)
|
||||
* \retval number of members
|
||||
* \retval -1 on error
|
||||
* \retval number of members
|
||||
* \retval -1 on error
|
||||
*/
|
||||
static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
|
@ -6435,7 +6600,8 @@ static struct ast_custom_function queuevar_function = {
|
|||
|
||||
static struct ast_custom_function queuemembercount_function = {
|
||||
.name = "QUEUE_MEMBER",
|
||||
.read = queue_function_qac,
|
||||
.read = queue_function_mem_read,
|
||||
.write = queue_function_mem_write,
|
||||
};
|
||||
|
||||
static struct ast_custom_function queuemembercount_dep = {
|
||||
|
@ -6494,6 +6660,7 @@ static int reload_queue_rules(int reload)
|
|||
while ((rulecat = ast_category_browse(cfg, rulecat))) {
|
||||
if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
|
||||
AST_LIST_UNLOCK(&rule_lists);
|
||||
ast_config_destroy(cfg);
|
||||
return AST_MODULE_LOAD_FAILURE;
|
||||
} else {
|
||||
ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
|
||||
|
@ -6517,8 +6684,9 @@ static void queue_set_global_params(struct ast_config *cfg)
|
|||
{
|
||||
const char *general_val = NULL;
|
||||
queue_persistent_members = 0;
|
||||
if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
|
||||
if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) {
|
||||
queue_persistent_members = ast_true(general_val);
|
||||
}
|
||||
autofill_default = 0;
|
||||
if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
|
||||
autofill_default = ast_true(general_val);
|
||||
|
@ -6533,6 +6701,9 @@ static void queue_set_global_params(struct ast_config *cfg)
|
|||
shared_lastcall = 0;
|
||||
if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
|
||||
shared_lastcall = ast_true(general_val);
|
||||
negative_penalty_invalid = 0;
|
||||
if ((general_val = ast_variable_retrieve(cfg, "general", "negative_penalty_invalid")))
|
||||
negative_penalty_invalid = ast_true(general_val);
|
||||
}
|
||||
|
||||
/*! \brief reload information pertaining to a single member
|
||||
|
@ -7087,9 +7258,15 @@ static int manager_queues_show(struct mansession *s, const struct message *m)
|
|||
static int manager_queue_rule_show(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *rule = astman_get_header(m, "Rule");
|
||||
const char *id = astman_get_header(m, "ActionID");
|
||||
struct rule_list *rl_iter;
|
||||
struct penalty_rule *pr_iter;
|
||||
|
||||
astman_append(s, "Response: Success\r\n");
|
||||
if (!ast_strlen_zero(id)) {
|
||||
astman_append(s, "ActionID: %s\r\n", id);
|
||||
}
|
||||
|
||||
AST_LIST_LOCK(&rule_lists);
|
||||
AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
|
||||
if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
|
||||
|
@ -7103,6 +7280,10 @@ static int manager_queue_rule_show(struct mansession *s, const struct message *m
|
|||
}
|
||||
AST_LIST_UNLOCK(&rule_lists);
|
||||
|
||||
/*
|
||||
* Two blank lines instead of one because the Response and
|
||||
* ActionID headers used to not be present.
|
||||
*/
|
||||
astman_append(s, "\r\n\r\n");
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<use>res_agi</use>
|
||||
<use type="module">res_agi</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
|
|
@ -38,8 +38,8 @@
|
|||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<use>res_adsi</use>
|
||||
<use>res_smdi</use>
|
||||
<use type="module">res_adsi</use>
|
||||
<use type="module">res_smdi</use>
|
||||
***/
|
||||
|
||||
/*** MAKEOPTS
|
||||
|
@ -60,7 +60,7 @@
|
|||
<depend>imap_tk</depend>
|
||||
<conflict>ODBC_STORAGE</conflict>
|
||||
<conflict>FILE_STORAGE</conflict>
|
||||
<use>openssl</use>
|
||||
<use type="external">openssl</use>
|
||||
<defaultenabled>no</defaultenabled>
|
||||
</member>
|
||||
</category>
|
||||
|
|
|
@ -284,6 +284,14 @@ static int set_bridge_option(const char *name, const char *value, struct bridge_
|
|||
}
|
||||
} else if (!strcasecmp(name, "record_conference")) {
|
||||
ast_set2_flag(b_profile, ast_true(value), BRIDGE_OPT_RECORD_CONFERENCE);
|
||||
} else if (!strcasecmp(name, "video_mode")) {
|
||||
if (!strcasecmp(value, "first_marked")) {
|
||||
ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED);
|
||||
} else if (!strcasecmp(value, "last_marked")) {
|
||||
ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED);
|
||||
} else if (!strcasecmp(value, "follow_talker")) {
|
||||
ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
|
||||
}
|
||||
} else if (!strcasecmp(name, "max_members")) {
|
||||
if (sscanf(value, "%30u", &b_profile->max_members) != 1) {
|
||||
return -1;
|
||||
|
@ -534,6 +542,7 @@ static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum con
|
|||
case MENU_ACTION_ADMIN_TOGGLE_LOCK:
|
||||
case MENU_ACTION_ADMIN_KICK_LAST:
|
||||
case MENU_ACTION_LEAVE:
|
||||
case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
|
||||
break;
|
||||
case MENU_ACTION_PLAYBACK:
|
||||
case MENU_ACTION_PLAYBACK_AND_CONTINUE:
|
||||
|
@ -649,6 +658,8 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
|
|||
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
|
||||
} else if (!strcasecmp(action, "leave_conference")) {
|
||||
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
|
||||
} else if (!strcasecmp(action, "set_as_single_video_src")) {
|
||||
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
|
||||
} else if (!strncasecmp(action, "dialplan_exec(", 14)) {
|
||||
ast_copy_string(buf, action, sizeof(buf));
|
||||
action_args = buf;
|
||||
|
@ -983,6 +994,16 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e,
|
|||
ast_cli(a->fd,"Max Members: No Limit\n");
|
||||
}
|
||||
|
||||
if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_LAST_MARKED) {
|
||||
ast_cli(a->fd, "Video Mode: last_marked\n");
|
||||
} else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) {
|
||||
ast_cli(a->fd, "Video Mode: first_marked\n");
|
||||
} else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER) {
|
||||
ast_cli(a->fd, "Video Mode: follow_talker\n");
|
||||
} else {
|
||||
ast_cli(a->fd, "Video Mode: no video\n");
|
||||
}
|
||||
|
||||
ast_cli(a->fd,"sound_join: %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
|
||||
ast_cli(a->fd,"sound_leave: %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
|
||||
ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
|
||||
|
@ -1142,6 +1163,9 @@ static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, s
|
|||
case MENU_ACTION_LEAVE:
|
||||
ast_cli(a->fd, "leave_conference");
|
||||
break;
|
||||
case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
|
||||
ast_cli(a->fd, "set_as_single_video_src");
|
||||
break;
|
||||
}
|
||||
action_num++;
|
||||
}
|
||||
|
|
|
@ -61,6 +61,9 @@ enum user_profile_flags {
|
|||
|
||||
enum bridge_profile_flags {
|
||||
BRIDGE_OPT_RECORD_CONFERENCE = (1 << 0), /*!< Set if the conference should be recorded */
|
||||
BRIDGE_OPT_VIDEO_SRC_LAST_MARKED = (1 << 1), /*!< Set if conference should feed video of last marked user to all participants. */
|
||||
BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED = (1 << 2), /*!< Set if conference should feed video of first marked user to all participants. */
|
||||
BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER = (1 << 3), /*!< Set if conference set the video feed to follow the loudest talker. */
|
||||
};
|
||||
|
||||
enum conf_menu_action_id {
|
||||
|
@ -78,6 +81,7 @@ enum conf_menu_action_id {
|
|||
MENU_ACTION_ADMIN_KICK_LAST,
|
||||
MENU_ACTION_LEAVE,
|
||||
MENU_ACTION_NOOP,
|
||||
MENU_ACTION_SET_SINGLE_VIDEO_SRC,
|
||||
};
|
||||
|
||||
/*! The conference menu action contains both
|
||||
|
|
|
@ -70,6 +70,20 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#define DEFAULT_SOFTMIX_SILENCE_THRESHOLD 2500
|
||||
#define DEFAULT_SOFTMIX_TALKING_THRESHOLD 160
|
||||
|
||||
#define DEFAULT_ENERGY_HISTORY_LEN 150
|
||||
|
||||
struct video_follow_talker_data {
|
||||
/*! audio energy history */
|
||||
int energy_history[DEFAULT_ENERGY_HISTORY_LEN];
|
||||
/*! The current slot being used in the history buffer, this
|
||||
* increments and wraps around */
|
||||
int energy_history_cur_slot;
|
||||
/*! The current energy sum used for averages. */
|
||||
int energy_accum;
|
||||
/*! The current energy average */
|
||||
int energy_average;
|
||||
};
|
||||
|
||||
/*! \brief Structure which contains per-channel mixing information */
|
||||
struct softmix_channel {
|
||||
/*! Lock to protect this structure */
|
||||
|
@ -93,6 +107,8 @@ struct softmix_channel {
|
|||
short final_buf[MAX_DATALEN];
|
||||
/*! Buffer containing only the audio from the channel */
|
||||
short our_buf[MAX_DATALEN];
|
||||
/*! Data pertaining to talker mode for video conferencing */
|
||||
struct video_follow_talker_data video_talker;
|
||||
};
|
||||
|
||||
struct softmix_bridge_data {
|
||||
|
@ -419,12 +435,24 @@ static void softmix_pass_dtmf(struct ast_bridge *bridge, struct ast_bridge_chann
|
|||
}
|
||||
}
|
||||
|
||||
static void softmix_pass_video(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
|
||||
{
|
||||
struct ast_bridge_channel *tmp;
|
||||
AST_LIST_TRAVERSE(&bridge->channels, tmp, entry) {
|
||||
if (tmp->suspended) {
|
||||
continue;
|
||||
}
|
||||
ast_write(tmp->chan, frame);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Function called when a channel writes a frame into the bridge */
|
||||
static enum ast_bridge_write_result softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
|
||||
{
|
||||
struct softmix_channel *sc = bridge_channel->bridge_pvt;
|
||||
struct softmix_bridge_data *softmix_data = bridge->bridge_pvt;
|
||||
int totalsilence = 0;
|
||||
int cur_energy = 0;
|
||||
int silence_threshold = bridge_channel->tech_args.silence_threshold ?
|
||||
bridge_channel->tech_args.silence_threshold :
|
||||
DEFAULT_SOFTMIX_SILENCE_THRESHOLD;
|
||||
|
@ -434,18 +462,52 @@ static enum ast_bridge_write_result softmix_bridge_write(struct ast_bridge *brid
|
|||
/* Only accept audio frames, all others are unsupported */
|
||||
if (frame->frametype == AST_FRAME_DTMF_END || frame->frametype == AST_FRAME_DTMF_BEGIN) {
|
||||
softmix_pass_dtmf(bridge, bridge_channel, frame);
|
||||
goto no_audio;
|
||||
} else if (frame->frametype != AST_FRAME_VOICE) {
|
||||
goto bridge_write_cleanup;
|
||||
} else if (frame->frametype != AST_FRAME_VOICE && frame->frametype != AST_FRAME_VIDEO) {
|
||||
res = AST_BRIDGE_WRITE_UNSUPPORTED;
|
||||
goto no_audio;
|
||||
goto bridge_write_cleanup;
|
||||
} else if (frame->datalen == 0) {
|
||||
goto no_audio;
|
||||
goto bridge_write_cleanup;
|
||||
}
|
||||
|
||||
/* Determine if this video frame should be distributed or not */
|
||||
if (frame->frametype == AST_FRAME_VIDEO) {
|
||||
switch (bridge->video_mode.mode) {
|
||||
case AST_BRIDGE_VIDEO_MODE_NONE:
|
||||
break;
|
||||
case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
|
||||
if (ast_bridge_is_video_src(bridge, bridge_channel->chan)) {
|
||||
softmix_pass_video(bridge, bridge_channel, frame);
|
||||
}
|
||||
break;
|
||||
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
|
||||
ast_mutex_lock(&sc->lock);
|
||||
ast_bridge_update_talker_src_video_mode(bridge, bridge_channel->chan, sc->video_talker.energy_average, ast_format_get_video_mark(&frame->subclass.format));
|
||||
ast_mutex_unlock(&sc->lock);
|
||||
if (ast_bridge_is_video_src(bridge, bridge_channel->chan)) {
|
||||
softmix_pass_video(bridge, bridge_channel, frame);
|
||||
}
|
||||
break;
|
||||
}
|
||||
goto bridge_write_cleanup;
|
||||
}
|
||||
|
||||
/* If we made it here, we are going to write the frame into the conference */
|
||||
ast_mutex_lock(&sc->lock);
|
||||
ast_dsp_silence_with_energy(sc->dsp, frame, &totalsilence, &cur_energy);
|
||||
|
||||
if (bridge->video_mode.mode == AST_BRIDGE_VIDEO_MODE_TALKER_SRC) {
|
||||
int cur_slot = sc->video_talker.energy_history_cur_slot;
|
||||
sc->video_talker.energy_accum -= sc->video_talker.energy_history[cur_slot];
|
||||
sc->video_talker.energy_accum += cur_energy;
|
||||
sc->video_talker.energy_history[cur_slot] = cur_energy;
|
||||
sc->video_talker.energy_average = sc->video_talker.energy_accum / DEFAULT_ENERGY_HISTORY_LEN;
|
||||
sc->video_talker.energy_history_cur_slot++;
|
||||
if (sc->video_talker.energy_history_cur_slot == DEFAULT_ENERGY_HISTORY_LEN) {
|
||||
sc->video_talker.energy_history_cur_slot = 0; /* wrap around */
|
||||
}
|
||||
}
|
||||
|
||||
ast_dsp_silence(sc->dsp, frame, &totalsilence);
|
||||
if (totalsilence < silence_threshold) {
|
||||
if (!sc->talking) {
|
||||
update_talking = 1;
|
||||
|
@ -487,7 +549,7 @@ static enum ast_bridge_write_result softmix_bridge_write(struct ast_bridge *brid
|
|||
|
||||
return res;
|
||||
|
||||
no_audio:
|
||||
bridge_write_cleanup:
|
||||
/* Even though the frame is not being written into the conference because it is not audio,
|
||||
* we should use this opportunity to check to see if a frame is ready to be written out from
|
||||
* the conference to the channel. */
|
||||
|
@ -817,7 +879,7 @@ softmix_cleanup:
|
|||
|
||||
static struct ast_bridge_technology softmix_bridge = {
|
||||
.name = "softmix",
|
||||
.capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX | AST_BRIDGE_CAPABILITY_THREAD | AST_BRIDGE_CAPABILITY_MULTITHREADED | AST_BRIDGE_CAPABILITY_OPTIMIZE,
|
||||
.capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX | AST_BRIDGE_CAPABILITY_THREAD | AST_BRIDGE_CAPABILITY_MULTITHREADED | AST_BRIDGE_CAPABILITY_OPTIMIZE | AST_BRIDGE_CAPABILITY_VIDEO,
|
||||
.preference = AST_BRIDGE_PREFERENCE_LOW,
|
||||
.create = softmix_bridge_create,
|
||||
.destroy = softmix_bridge_destroy,
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
# It will be executed from the top-level directory of the project.
|
||||
|
||||
make -C sounds MENUSELECT_CORE_SOUNDS=CORE-SOUNDS-EN-GSM MENUSELECT_MOH=MOH-OPSOUND-WAV WGET=wget DOWNLOAD=wget all
|
||||
make AWK=awk GREP=grep menuselect-tree
|
||||
|
||||
if ! which wikiexport.py 2>&1 > /dev/null ; then
|
||||
echo
|
||||
|
|
|
@ -229,6 +229,9 @@ static struct ast_channel *bridge_request(const char *type, struct ast_format_ca
|
|||
ast_format_copy(&p->input->rawwriteformat, &slin);
|
||||
ast_format_copy(&p->output->rawwriteformat, &slin);
|
||||
|
||||
ast_answer(p->output);
|
||||
ast_answer(p->input);
|
||||
|
||||
return p->input;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,12 +38,12 @@
|
|||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<use>res_smdi</use>
|
||||
<use type="module">res_smdi</use>
|
||||
<depend>dahdi</depend>
|
||||
<depend>tonezone</depend>
|
||||
<use>pri</use>
|
||||
<use>ss7</use>
|
||||
<use>openr2</use>
|
||||
<use type="external">pri</use>
|
||||
<use type="external">ss7</use>
|
||||
<use type="external">openr2</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
@ -9491,6 +9491,8 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
|
|||
int features;
|
||||
struct ast_str *chan_name;
|
||||
struct ast_variable *v;
|
||||
char *dashptr;
|
||||
char device_name[AST_CHANNEL_NAME];
|
||||
|
||||
if (i->subs[idx].owner) {
|
||||
ast_log(LOG_WARNING, "Channel %d already has a %s call\n", i->channel,subnames[idx]);
|
||||
|
@ -9672,7 +9674,13 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
|
|||
/* Configure the new channel jb */
|
||||
ast_jb_configure(tmp, &global_jbconf);
|
||||
|
||||
ast_devstate_changed_literal(ast_state_chan2dev(state), tmp->name);
|
||||
/* Set initial device state */
|
||||
ast_copy_string(device_name, tmp->name, sizeof(device_name));
|
||||
dashptr = strrchr(device_name, '-');
|
||||
if (dashptr) {
|
||||
*dashptr = '\0';
|
||||
}
|
||||
ast_devstate_changed_literal(AST_DEVICE_UNKNOWN, device_name);
|
||||
|
||||
for (v = i->vars ; v ; v = v->next)
|
||||
pbx_builtin_setvar_helper(tmp, v->name, v->value);
|
||||
|
@ -13933,17 +13941,22 @@ static void *mfcr2_monitor(void *data)
|
|||
#endif
|
||||
static void dahdi_pri_message(struct pri *pri, char *s)
|
||||
{
|
||||
int x, y;
|
||||
int dchan = -1, span = -1, dchancount = 0;
|
||||
int x;
|
||||
int y;
|
||||
int dchan = -1;
|
||||
int span = -1;
|
||||
int dchancount = 0;
|
||||
|
||||
if (pri) {
|
||||
for (x = 0; x < NUM_SPANS; x++) {
|
||||
for (y = 0; y < SIG_PRI_NUM_DCHANS; y++) {
|
||||
if (pris[x].pri.dchans[y])
|
||||
if (pris[x].pri.dchans[y]) {
|
||||
dchancount++;
|
||||
}
|
||||
|
||||
if (pris[x].pri.dchans[y] == pri)
|
||||
if (pris[x].pri.dchans[y] == pri) {
|
||||
dchan = y;
|
||||
}
|
||||
}
|
||||
if (dchan >= 0) {
|
||||
span = x;
|
||||
|
@ -13951,14 +13964,18 @@ static void dahdi_pri_message(struct pri *pri, char *s)
|
|||
}
|
||||
dchancount = 0;
|
||||
}
|
||||
if (dchancount > 1 && (span > -1))
|
||||
ast_verbose("[Span %d D-Channel %d]%s", span, dchan, s);
|
||||
else if (span > -1)
|
||||
ast_verbose("%d %s", span+1, s);
|
||||
else
|
||||
ast_verbose("%s", s);
|
||||
} else
|
||||
ast_verbose("%s", s);
|
||||
if (-1 < span) {
|
||||
if (1 < dchancount) {
|
||||
ast_verbose("[PRI Span: %d D-Channel: %d] %s", span + 1, dchan, s);
|
||||
} else {
|
||||
ast_verbose("PRI Span: %d %s", span + 1, s);
|
||||
}
|
||||
} else {
|
||||
ast_verbose("PRI Span: ? %s", s);
|
||||
}
|
||||
} else {
|
||||
ast_verbose("PRI Span: ? %s", s);
|
||||
}
|
||||
|
||||
ast_mutex_lock(&pridebugfdlock);
|
||||
|
||||
|
@ -13975,18 +13992,22 @@ static void dahdi_pri_message(struct pri *pri, char *s)
|
|||
#if defined(HAVE_PRI)
|
||||
static void dahdi_pri_error(struct pri *pri, char *s)
|
||||
{
|
||||
int x, y;
|
||||
int dchan = -1, span = -1;
|
||||
int x;
|
||||
int y;
|
||||
int dchan = -1;
|
||||
int span = -1;
|
||||
int dchancount = 0;
|
||||
|
||||
if (pri) {
|
||||
for (x = 0; x < NUM_SPANS; x++) {
|
||||
for (y = 0; y < SIG_PRI_NUM_DCHANS; y++) {
|
||||
if (pris[x].pri.dchans[y])
|
||||
if (pris[x].pri.dchans[y]) {
|
||||
dchancount++;
|
||||
}
|
||||
|
||||
if (pris[x].pri.dchans[y] == pri)
|
||||
if (pris[x].pri.dchans[y] == pri) {
|
||||
dchan = y;
|
||||
}
|
||||
}
|
||||
if (dchan >= 0) {
|
||||
span = x;
|
||||
|
@ -13994,14 +14015,18 @@ static void dahdi_pri_error(struct pri *pri, char *s)
|
|||
}
|
||||
dchancount = 0;
|
||||
}
|
||||
if ((dchancount > 1) && (span > -1))
|
||||
ast_log(LOG_ERROR, "[Span %d D-Channel %d] PRI: %s", span, dchan, s);
|
||||
else if (span > -1)
|
||||
ast_log(LOG_ERROR, "%d %s", span+1, s);
|
||||
else
|
||||
ast_log(LOG_ERROR, "%s", s);
|
||||
} else
|
||||
ast_log(LOG_ERROR, "%s", s);
|
||||
if (-1 < span) {
|
||||
if (1 < dchancount) {
|
||||
ast_log(LOG_ERROR, "[PRI Span: %d D-Channel: %d] %s", span + 1, dchan, s);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "PRI Span: %d %s", span + 1, s);
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "PRI Span: ? %s", s);
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "PRI Span: ? %s", s);
|
||||
}
|
||||
|
||||
ast_mutex_lock(&pridebugfdlock);
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
/*** MODULEINFO
|
||||
<depend>iksemel</depend>
|
||||
<depend>res_jabber</depend>
|
||||
<use>openssl</use>
|
||||
<use type="external">openssl</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<use>crypto</use>
|
||||
<use type="external">crypto</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
@ -5363,10 +5363,6 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
|
|||
/* these two cannot be sent, because they require a result */
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
case AST_OPTION_FORMAT_READ:
|
||||
case AST_OPTION_FORMAT_WRITE:
|
||||
case AST_OPTION_MAKE_COMPATIBLE:
|
||||
return -1;
|
||||
case AST_OPTION_OPRMODE:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
|
@ -5383,7 +5379,16 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
|
|||
ast_mutex_unlock(&iaxsl[callno]);
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
/* These options are sent to the other side across the network where
|
||||
* they will be passed to whatever channel is bridged there. Don't
|
||||
* do anything silly like pass an option that transmits pointers to
|
||||
* memory on this machine to a remote machine to use */
|
||||
case AST_OPTION_TONE_VERIFY:
|
||||
case AST_OPTION_TDD:
|
||||
case AST_OPTION_RELAXDTMF:
|
||||
case AST_OPTION_AUDIO_MODE:
|
||||
case AST_OPTION_DIGIT_DETECT:
|
||||
case AST_OPTION_FAX_DETECT:
|
||||
{
|
||||
unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
|
||||
struct chan_iax2_pvt *pvt;
|
||||
|
@ -5411,7 +5416,12 @@ static int iax2_setoption(struct ast_channel *c, int option, void *data, int dat
|
|||
ast_free(h);
|
||||
return res;
|
||||
}
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Just in case someone does a break instead of a return */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int iax2_queryoption(struct ast_channel *c, int option, void *data, int *datalen)
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
/*** MODULEINFO
|
||||
<depend>iksemel</depend>
|
||||
<depend>res_jabber</depend>
|
||||
<use>openssl</use>
|
||||
<use type="external">openssl</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<use>res_pktccops</use>
|
||||
<use type="module">res_pktccops</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -745,7 +745,6 @@ struct sip_socket {
|
|||
struct sip_request {
|
||||
ptrdiff_t rlPart1; /*!< Offset of the SIP Method Name or "SIP/2.0" protocol version */
|
||||
ptrdiff_t rlPart2; /*!< Offset of the Request URI or Response Status */
|
||||
int len; /*!< bytes used in data[], excluding trailing null terminator. Rarely used. */
|
||||
int headers; /*!< # of SIP Headers */
|
||||
int method; /*!< Method of this request */
|
||||
int lines; /*!< Body Content */
|
||||
|
@ -830,11 +829,16 @@ struct sip_history {
|
|||
|
||||
/*! \brief sip_auth: Credentials for authentication to other SIP services */
|
||||
struct sip_auth {
|
||||
AST_LIST_ENTRY(sip_auth) node;
|
||||
char realm[AST_MAX_EXTENSION]; /*!< Realm in which these credentials are valid */
|
||||
char username[256]; /*!< Username */
|
||||
char secret[256]; /*!< Secret */
|
||||
char md5secret[256]; /*!< MD5Secret */
|
||||
struct sip_auth *next; /*!< Next auth structure in list */
|
||||
};
|
||||
|
||||
/*! \brief Container of SIP authentication credentials. */
|
||||
struct sip_auth_container {
|
||||
AST_LIST_HEAD_NOLOCK(, sip_auth) list;
|
||||
};
|
||||
|
||||
/*! \brief T.38 channel settings (at some point we need to make this alloc'ed */
|
||||
|
@ -1047,7 +1051,7 @@ struct sip_pvt {
|
|||
struct ast_channel *owner; /*!< Who owns us (if we have an owner) */
|
||||
struct sip_route *route; /*!< Head of linked list of routing steps (fm Record-Route) */
|
||||
struct sip_notify *notify; /*!< Custom notify type */
|
||||
struct sip_auth *peerauth; /*!< Realm authentication */
|
||||
struct sip_auth_container *peerauth;/*!< Realm authentication credentials */
|
||||
int noncecount; /*!< Nonce-count */
|
||||
unsigned int stalenonce:1; /*!< Marks the current nonce as responded too */
|
||||
char lastmsg[256]; /*!< Last Message sent/received */
|
||||
|
@ -1144,7 +1148,6 @@ struct sip_pkt {
|
|||
struct timeval time_sent; /*!< When pkt was sent */
|
||||
int64_t retrans_stop_time; /*!< Time in ms after 'now' that retransmission must stop */
|
||||
int retrans_stop; /*!< Timeout is reached, stop retransmission */
|
||||
int packetlen; /*!< Length of packet */
|
||||
struct ast_str *data;
|
||||
};
|
||||
|
||||
|
@ -1211,7 +1214,7 @@ struct sip_peer {
|
|||
* for incoming calls
|
||||
*/
|
||||
unsigned short deprecated_username:1; /*!< If it's a realtime peer, are they using the deprecated "username" instead of "defaultuser" */
|
||||
struct sip_auth *auth; /*!< Realm authentication list */
|
||||
struct sip_auth_container *auth;/*!< Realm authentication credentials */
|
||||
int amaflags; /*!< AMA Flags (for billing) */
|
||||
int callingpres; /*!< Calling id presentation */
|
||||
int inUse; /*!< Number of calls in use */
|
||||
|
|
|
@ -1029,14 +1029,14 @@ int get_in_brackets_full(char *tmp,char **out,char **residue)
|
|||
only affects token based display-names there is no danger of brackets being in quotes */
|
||||
if (first_bracket) {
|
||||
parse = first_bracket;
|
||||
} else {
|
||||
} else {
|
||||
parse = tmp;
|
||||
}
|
||||
|
||||
if ((second_bracket = strchr(parse, '>'))) {
|
||||
*second_bracket++ = '\0';
|
||||
if (out) {
|
||||
*out = first_bracket;
|
||||
*out = (char *) parse;
|
||||
}
|
||||
if (residue) {
|
||||
*residue = second_bracket;
|
||||
|
@ -1045,9 +1045,9 @@ int get_in_brackets_full(char *tmp,char **out,char **residue)
|
|||
}
|
||||
|
||||
if ((first_bracket)) {
|
||||
ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
|
||||
ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (out) {
|
||||
*out = tmp;
|
||||
|
@ -1076,6 +1076,7 @@ AST_TEST_DEFINE(get_in_brackets_test)
|
|||
char name_no_quotes[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
|
||||
char no_end_bracket[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah";
|
||||
char no_name_no_brackets[] = "sip:name@host";
|
||||
char missing_start_bracket[] = "name not in quotes sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
|
||||
char *uri = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
|
@ -1140,6 +1141,13 @@ AST_TEST_DEFINE(get_in_brackets_test)
|
|||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
/* Test 8, no start bracket, but with ending bracket. */
|
||||
if (!(uri = get_in_brackets(missing_start_bracket)) || !(strcmp(uri, in_brackets))) {
|
||||
|
||||
ast_test_status_update(test, "Test 8 failed. %s\n", uri);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
/*** MODULEINFO
|
||||
<depend>speex</depend>
|
||||
<depend>speex_preprocess</depend>
|
||||
<use>speexdsp</use>
|
||||
<use type="external">speexdsp</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
|
|
@ -168,6 +168,26 @@ type=bridge
|
|||
; larger amounts of delay into the bridge. Valid values here are 10, 20, 40,
|
||||
; or 80. By default 20ms is used.
|
||||
|
||||
;video_mode = follow_talker ; Sets how confbridge handles video distribution to the conference participants.
|
||||
; Note that participants wanting to view and be the source of a video feed
|
||||
; _MUST_ be sharing the same video codec.
|
||||
; --- MODES ---
|
||||
; none: No video sources are set by default in the conference. It is still
|
||||
; possible for a user to be set as a video source via AMI or DTMF action
|
||||
; at any time.
|
||||
;
|
||||
; follow_talker: The video feed will follow whoever is talking and providing video.
|
||||
;
|
||||
; last_marked: The last marked user to join the conference with video capabilities
|
||||
; will be the single source of video distributed to all participants.
|
||||
; If multiple marked users are capable of video, the last one to join
|
||||
; is always the source, when that user leaves it goes to the one who
|
||||
; joined before them.
|
||||
;
|
||||
; first_marked: The first marked user to join the conference with video capabilities
|
||||
; is the single source of video distribution among all participants. If
|
||||
; that user leaves, the marked user to join after them becomes the source.
|
||||
|
||||
; All sounds in the conference are customizable using the bridge profile options below.
|
||||
; Simply state the option followed by the filename or full path of the filename after
|
||||
; the option. Example: sound_had_joined=conf-hasjoin This will play the conf-hasjoin
|
||||
|
@ -264,6 +284,8 @@ type=bridge
|
|||
; admin_toggle_conference_lock ; This action allows an Admin to toggle locking and
|
||||
; unlocking the conference. Non admins can not use
|
||||
; this action even if it is in their menu.
|
||||
; set_as_single_video_src ; This action allows any user to set themselves as the
|
||||
; single video source distributed to all participants.
|
||||
|
||||
[sample_user_menu]
|
||||
type=menu
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
;
|
||||
; 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 the caller's stay in the queue, these will not be reflected for that caller.
|
||||
; 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]
|
||||
|
|
|
@ -61,6 +61,10 @@ monitor-type = MixMonitor
|
|||
;
|
||||
;shared_lastcall=no
|
||||
;
|
||||
; Negative_penalty_invalid will treat members with a negative penalty as logged off
|
||||
;
|
||||
;negative_penalty_invalid = no
|
||||
;
|
||||
;[markq]
|
||||
;
|
||||
; A sample call queue
|
||||
|
@ -196,6 +200,10 @@ monitor-type = MixMonitor
|
|||
; all: Memeber will be paused in all queues he/she is a member
|
||||
;autopause=yes
|
||||
;
|
||||
; Autopausedelay delay autopause for autopausedelay seconds from the
|
||||
; last call if a member has not taken a call the delay has no effect.
|
||||
;autopausedelay=60
|
||||
;
|
||||
; Maximum number of people waiting in the queue (0 for unlimited)
|
||||
;
|
||||
;maxlen = 0
|
||||
|
@ -459,6 +467,9 @@ monitor-type = MixMonitor
|
|||
; uncomment this option. (Note: only the SIP channel driver currently is able
|
||||
; to report 'in use'.)
|
||||
;
|
||||
; A member can have the ignorebusy flag set or unset when ringinuse is set to
|
||||
; allow a per member control.
|
||||
;
|
||||
; ringinuse = no
|
||||
;
|
||||
; If you wish to have a delay before the member is connected to the caller (or
|
||||
|
|
|
@ -130,7 +130,7 @@ allowoverlap=no ; Disable overlap dialing support. (Default is y
|
|||
; asterisk.conf, it defaults to that system name
|
||||
; Realms MUST be globally unique according to RFC 3261
|
||||
; Set this to your host name or domain name
|
||||
;domainsasrealm=no ; Use domans list as realms
|
||||
;domainsasrealm=no ; Use domains list as realms
|
||||
; You can serve multiple Realms specifying several
|
||||
; 'domain=...' directives (see below).
|
||||
; In this case Realm will be based on request 'From'/'To' header
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#/bin/bash
|
||||
|
||||
# Script written by Trey Blancher (support@digium.com)
|
||||
|
||||
# This script is designed to convert all files of type $SRC to
|
||||
# the $DST format, for the given $LANGUAGE. It traverses the given
|
||||
# language directory (by default in /var/lib/asterisk/sounds/), and
|
||||
# converts each file with filename extension $SRC, and converts them
|
||||
# using Asterisk to files with type and extension $DST.
|
||||
|
||||
LANGUAGE=en # change accordingly, if converting custom sounds you may want to omit this variable
|
||||
SRC=gsm # change accordingly (e.g. to wav, etc.)
|
||||
DST=g729 # change accordingly (e.g. to wav, etc.)
|
||||
SOUNDS=/var/lib/asterisk/sounds # for custom sounds change this directory to your custom sound directory
|
||||
|
||||
for file in $(find ${SOUNDS}/${LANGUAGE}/ -depth -type f -name *.${SRC});
|
||||
do
|
||||
#echo $file
|
||||
asterisk -rx "file convert $file $(dirname $file)/$(basename $file $SRC)$DST"
|
||||
done
|
|
@ -23,7 +23,7 @@
|
|||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<use>crypto</use>
|
||||
<use type="external">crypto</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
/*** MODULEINFO
|
||||
<depend>speex</depend>
|
||||
<depend>speex_preprocess</depend>
|
||||
<use>speexdsp</use>
|
||||
<use type="external">speexdsp</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
|
|
@ -167,12 +167,48 @@ struct ast_bridge_channel {
|
|||
AST_LIST_ENTRY(ast_bridge_channel) entry;
|
||||
};
|
||||
|
||||
enum ast_bridge_video_mode_type {
|
||||
/*! Video is not allowed in the bridge */
|
||||
AST_BRIDGE_VIDEO_MODE_NONE = 0,
|
||||
/*! A single user is picked as the only distributed of video across the bridge */
|
||||
AST_BRIDGE_VIDEO_MODE_SINGLE_SRC,
|
||||
/*! A single user's video feed is distributed to all bridge channels, but
|
||||
* that feed is automatically picked based on who is talking the most. */
|
||||
AST_BRIDGE_VIDEO_MODE_TALKER_SRC,
|
||||
};
|
||||
|
||||
/*! This is used for both SINGLE_SRC mode to set what channel
|
||||
* should be the current single video feed */
|
||||
struct ast_bridge_video_single_src_data {
|
||||
/*! Only accept video coming from this channel */
|
||||
struct ast_channel *chan_vsrc;
|
||||
};
|
||||
|
||||
/*! This is used for both SINGLE_SRC_TALKER mode to set what channel
|
||||
* should be the current single video feed */
|
||||
struct ast_bridge_video_talker_src_data {
|
||||
/*! Only accept video coming from this channel */
|
||||
struct ast_channel *chan_vsrc;
|
||||
int average_talking_energy;
|
||||
};
|
||||
|
||||
struct ast_bridge_video_mode {
|
||||
enum ast_bridge_video_mode_type mode;
|
||||
/* Add data for all the video modes here. */
|
||||
union {
|
||||
struct ast_bridge_video_single_src_data single_src_data;
|
||||
struct ast_bridge_video_talker_src_data talker_src_data;
|
||||
} mode_data;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Structure that contains information about a bridge
|
||||
*/
|
||||
struct ast_bridge {
|
||||
/*! Number of channels participating in the bridge */
|
||||
int num;
|
||||
/*! The video mode this bridge is using */
|
||||
struct ast_bridge_video_mode video_mode;
|
||||
/*! The internal sample rate this bridge is mixed at when multiple channels are being mixed.
|
||||
* If this value is 0, the bridge technology may auto adjust the internal mixing rate. */
|
||||
unsigned int internal_sample_rate;
|
||||
|
@ -475,6 +511,31 @@ void ast_bridge_set_internal_sample_rate(struct ast_bridge *bridge, unsigned int
|
|||
*/
|
||||
void ast_bridge_set_mixing_interval(struct ast_bridge *bridge, unsigned int mixing_interval);
|
||||
|
||||
/*!
|
||||
* \brief Set a bridge to feed a single video source to all participants.
|
||||
*/
|
||||
void ast_bridge_set_single_src_video_mode(struct ast_bridge *bridge, struct ast_channel *video_src_chan);
|
||||
|
||||
/*!
|
||||
* \brief Set the bridge to pick the strongest talker supporting
|
||||
* video as the single source video feed
|
||||
*/
|
||||
void ast_bridge_set_talker_src_video_mode(struct ast_bridge *bridge);
|
||||
|
||||
/*!
|
||||
* \brief Update information about talker energy for talker src video mode.
|
||||
*/
|
||||
void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct ast_channel *chan, int talker_energy, int is_keyfame);
|
||||
|
||||
/*!
|
||||
* \brief Determine if a channel is a video src for the bridge
|
||||
*/
|
||||
int ast_bridge_is_video_src(struct ast_bridge *bridge, struct ast_channel *chan);
|
||||
|
||||
/*!
|
||||
* \brief remove a channel as a source of video for the bridge.
|
||||
*/
|
||||
void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *chan);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
|
|
|
@ -109,6 +109,11 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
|
|||
number of seconds of silence */
|
||||
int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence);
|
||||
|
||||
/*! \brief Return non-zero if this is silence. Updates "totalsilence" with the total
|
||||
number of seconds of silence. Returns the average energy of the samples in the frame
|
||||
in frames_energy variable. */
|
||||
int ast_dsp_silence_with_energy(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence, int *frames_energy);
|
||||
|
||||
/*!
|
||||
* \brief Return non-zero if this is noise. Updates "totalnoise" with the total
|
||||
* number of seconds of noise
|
||||
|
|
|
@ -334,45 +334,51 @@ enum ast_control_transfer {
|
|||
#define AST_OPTION_FLAG_WTF 6
|
||||
|
||||
/*! Verify touchtones by muting audio transmission
|
||||
(and reception) and verify the tone is still present */
|
||||
* (and reception) and verify the tone is still present
|
||||
* Option data is a single signed char value 0 or 1 */
|
||||
#define AST_OPTION_TONE_VERIFY 1
|
||||
|
||||
/*! Put a compatible channel into TDD (TTY for the hearing-impared) mode */
|
||||
/*! Put a compatible channel into TDD (TTY for the hearing-impared) mode
|
||||
* Option data is a single signed char value 0 or 1 */
|
||||
#define AST_OPTION_TDD 2
|
||||
|
||||
/*! Relax the parameters for DTMF reception (mainly for radio use) */
|
||||
/*! Relax the parameters for DTMF reception (mainly for radio use)
|
||||
* Option data is a single signed char value 0 or 1 */
|
||||
#define AST_OPTION_RELAXDTMF 3
|
||||
|
||||
/*! Set (or clear) Audio (Not-Clear) Mode */
|
||||
/*! Set (or clear) Audio (Not-Clear) Mode
|
||||
* Option data is a single signed char value 0 or 1 */
|
||||
#define AST_OPTION_AUDIO_MODE 4
|
||||
|
||||
/*! Set channel transmit gain
|
||||
* Option data is a single signed char
|
||||
representing number of decibels (dB)
|
||||
to set gain to (on top of any gain
|
||||
specified in channel driver)
|
||||
*/
|
||||
* Option data is a single signed char representing number of decibels (dB)
|
||||
* to set gain to (on top of any gain specified in channel driver) */
|
||||
#define AST_OPTION_TXGAIN 5
|
||||
|
||||
/*! Set channel receive gain
|
||||
* Option data is a single signed char
|
||||
representing number of decibels (dB)
|
||||
to set gain to (on top of any gain
|
||||
specified in channel driver)
|
||||
*/
|
||||
* Option data is a single signed char representing number of decibels (dB)
|
||||
* to set gain to (on top of any gain specified in channel driver) */
|
||||
#define AST_OPTION_RXGAIN 6
|
||||
|
||||
/* set channel into "Operator Services" mode */
|
||||
/* set channel into "Operator Services" mode
|
||||
* Option data is a struct oprmode
|
||||
*
|
||||
* \note This option should never be sent over the network */
|
||||
#define AST_OPTION_OPRMODE 7
|
||||
|
||||
/*! Explicitly enable or disable echo cancelation for the given channel */
|
||||
/*! Explicitly enable or disable echo cancelation for the given channel
|
||||
* Option data is a single signed char value 0 or 1
|
||||
*
|
||||
* \note This option appears to be unused in the code. It is handled, but never
|
||||
* set or queried. */
|
||||
#define AST_OPTION_ECHOCAN 8
|
||||
|
||||
/*! \brief Handle channel write data
|
||||
* If a channel needs to process the data from a func_channel write operation
|
||||
* after func_channel_write executes, it can define the setoption callback
|
||||
* and process this option. A pointer to an ast_chan_write_info_t will be passed.
|
||||
* */
|
||||
*
|
||||
* \note This option should never be passed over the network. */
|
||||
#define AST_OPTION_CHANNEL_WRITE 9
|
||||
|
||||
/* !
|
||||
|
@ -381,28 +387,38 @@ enum ast_control_transfer {
|
|||
*/
|
||||
#define AST_OPTION_T38_STATE 10
|
||||
|
||||
/*! Request that the channel driver deliver frames in a specific format */
|
||||
/*! Request that the channel driver deliver frames in a specific format
|
||||
* Option data is a format_t */
|
||||
#define AST_OPTION_FORMAT_READ 11
|
||||
|
||||
/*! Request that the channel driver be prepared to accept frames in a specific format */
|
||||
/*! Request that the channel driver be prepared to accept frames in a specific format
|
||||
* Option data is a format_t */
|
||||
#define AST_OPTION_FORMAT_WRITE 12
|
||||
|
||||
/*! Request that the channel driver make two channels of the same tech type compatible if possible */
|
||||
/*! Request that the channel driver make two channels of the same tech type compatible if possible
|
||||
* Option data is an ast_channel
|
||||
*
|
||||
* \note This option should never be passed over the network */
|
||||
#define AST_OPTION_MAKE_COMPATIBLE 13
|
||||
|
||||
/*! Get or set the digit detection state of the channel */
|
||||
/*! Get or set the digit detection state of the channel
|
||||
* Option data is a single signed char value 0 or 1 */
|
||||
#define AST_OPTION_DIGIT_DETECT 14
|
||||
|
||||
/*! Get or set the fax tone detection state of the channel */
|
||||
/*! Get or set the fax tone detection state of the channel
|
||||
* Option data is a single signed char value 0 or 1 */
|
||||
#define AST_OPTION_FAX_DETECT 15
|
||||
|
||||
/*! Get the device name from the channel */
|
||||
/*! Get the device name from the channel (Read only)
|
||||
* Option data is a character buffer of suitable length */
|
||||
#define AST_OPTION_DEVICE_NAME 16
|
||||
|
||||
/*! Get the CC agent type from the channel */
|
||||
/*! Get the CC agent type from the channel (Read only)
|
||||
* Option data is a character buffer of suitable length */
|
||||
#define AST_OPTION_CC_AGENT_TYPE 17
|
||||
|
||||
/*! Get or set the security options on a channel */
|
||||
/*! Get or set the security options on a channel
|
||||
* Option data is an integer value of 0 or 1 */
|
||||
#define AST_OPTION_SECURE_SIGNALING 18
|
||||
#define AST_OPTION_SECURE_MEDIA 19
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ void ast_console_toggle_loglevel(int fd, int level, int state);
|
|||
#endif
|
||||
#define AST_LOG_DTMF __LOG_DTMF, _A_
|
||||
|
||||
#define NUMLOGLEVELS 6
|
||||
#define NUMLOGLEVELS 7
|
||||
|
||||
/*!
|
||||
* \brief Get the debug level for a module
|
||||
|
|
|
@ -152,8 +152,13 @@ int ast_sockaddr_cmp_addr(const struct ast_sockaddr *a, const struct ast_sockadd
|
|||
#define AST_SOCKADDR_STR_ADDR (1 << 0)
|
||||
#define AST_SOCKADDR_STR_PORT (1 << 1)
|
||||
#define AST_SOCKADDR_STR_BRACKETS (1 << 2)
|
||||
#define AST_SOCKADDR_STR_HOST AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_BRACKETS
|
||||
#define AST_SOCKADDR_STR_DEFAULT AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_PORT
|
||||
#define AST_SOCKADDR_STR_REMOTE (1 << 3)
|
||||
#define AST_SOCKADDR_STR_HOST (AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_BRACKETS)
|
||||
#define AST_SOCKADDR_STR_DEFAULT (AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_PORT)
|
||||
#define AST_SOCKADDR_STR_ADDR_REMOTE (AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_REMOTE)
|
||||
#define AST_SOCKADDR_STR_HOST_REMOTE (AST_SOCKADDR_STR_HOST | AST_SOCKADDR_STR_REMOTE)
|
||||
#define AST_SOCKADDR_STR_DEFAULT_REMOTE (AST_SOCKADDR_STR_DEFAULT | AST_SOCKADDR_STR_REMOTE)
|
||||
#define AST_SOCKADDR_STR_FORMAT_MASK (AST_SOCKADDR_STR_ADDR | AST_SOCKADDR_STR_PORT | AST_SOCKADDR_STR_BRACKETS)
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
|
@ -199,6 +204,23 @@ static inline char *ast_sockaddr_stringify(const struct ast_sockaddr *addr)
|
|||
return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_DEFAULT);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
*
|
||||
* \brief
|
||||
* Wrapper around ast_sockaddr_stringify_fmt() with default format
|
||||
*
|
||||
* \note This address will be suitable for passing to a remote machine via the
|
||||
* application layer. For example, the scope-id on a link-local IPv6 address
|
||||
* will be stripped.
|
||||
*
|
||||
* \return same as ast_sockaddr_stringify_fmt()
|
||||
*/
|
||||
static inline char *ast_sockaddr_stringify_remote(const struct ast_sockaddr *addr)
|
||||
{
|
||||
return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_DEFAULT_REMOTE);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
*
|
||||
|
@ -212,6 +234,23 @@ static inline char *ast_sockaddr_stringify_addr(const struct ast_sockaddr *addr)
|
|||
return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_ADDR);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
*
|
||||
* \brief
|
||||
* Wrapper around ast_sockaddr_stringify_fmt() to return an address only
|
||||
*
|
||||
* \note This address will be suitable for passing to a remote machine via the
|
||||
* application layer. For example, the scope-id on a link-local IPv6 address
|
||||
* will be stripped.
|
||||
*
|
||||
* \return same as ast_sockaddr_stringify_fmt()
|
||||
*/
|
||||
static inline char *ast_sockaddr_stringify_addr_remote(const struct ast_sockaddr *addr)
|
||||
{
|
||||
return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_ADDR_REMOTE);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
*
|
||||
|
@ -226,6 +265,24 @@ static inline char *ast_sockaddr_stringify_host(const struct ast_sockaddr *addr)
|
|||
return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_HOST);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
*
|
||||
* \brief
|
||||
* Wrapper around ast_sockaddr_stringify_fmt() to return an address only,
|
||||
* suitable for a URL (with brackets for IPv6).
|
||||
*
|
||||
* \note This address will be suitable for passing to a remote machine via the
|
||||
* application layer. For example, the scope-id on a link-local IPv6 address
|
||||
* will be stripped.
|
||||
*
|
||||
* \return same as ast_sockaddr_stringify_fmt()
|
||||
*/
|
||||
static inline char *ast_sockaddr_stringify_host_remote(const struct ast_sockaddr *addr)
|
||||
{
|
||||
return ast_sockaddr_stringify_fmt(addr, AST_SOCKADDR_STR_HOST_REMOTE);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
*
|
||||
|
@ -409,6 +466,20 @@ int ast_sockaddr_is_ipv4_mapped(const struct ast_sockaddr *addr);
|
|||
*/
|
||||
int ast_sockaddr_is_ipv4_multicast(const struct ast_sockaddr *addr);
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
*
|
||||
* \brief
|
||||
* Determine if this is a link-local IPv6 address
|
||||
*
|
||||
* \warning You should rarely need this function. Only use if you know what
|
||||
* you're doing.
|
||||
*
|
||||
* \retval 1 This is a link-local IPv6 address.
|
||||
* \retval 0 This is link-local IPv6 address.
|
||||
*/
|
||||
int ast_sockaddr_is_ipv6_link_local(const struct ast_sockaddr *addr);
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
*
|
||||
|
|
|
@ -877,6 +877,8 @@ int ast_context_unlockmacro(const char *macrocontext);
|
|||
/*!
|
||||
* \brief Set the channel to next execute the specified dialplan location.
|
||||
* \see ast_async_parseable_goto, ast_async_goto_if_exists
|
||||
*
|
||||
* \note Do _NOT_ hold any channel locks when calling this function.
|
||||
*/
|
||||
int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority);
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ enum ast_fax_capabilities {
|
|||
AST_FAX_TECH_T38 = (1 << 3),
|
||||
/*! sending mulitple documents supported */
|
||||
AST_FAX_TECH_MULTI_DOC = (1 << 4),
|
||||
/*! T.38 - T.30 Gateway */
|
||||
AST_FAX_TECH_GATEWAY = (1 << 5),
|
||||
};
|
||||
|
||||
/*! \brief fax modem capabilities */
|
||||
|
@ -168,6 +170,8 @@ struct ast_fax_session_details {
|
|||
struct ast_fax_t38_parameters our_t38_parameters;
|
||||
/*! the other endpoint's T.38 session parameters, if any */
|
||||
struct ast_fax_t38_parameters their_t38_parameters;
|
||||
/*! the id of the t.38 gateway framehook for this channel */
|
||||
int gateway_id;
|
||||
};
|
||||
|
||||
struct ast_fax_tech;
|
||||
|
@ -204,6 +208,9 @@ struct ast_fax_session {
|
|||
struct ast_smoother *smoother;
|
||||
};
|
||||
|
||||
/* if this overlaps with any AST_FRFLAG_* values, problems will occur */
|
||||
#define AST_FAX_FRFLAG_GATEWAY (1 << 13)
|
||||
|
||||
/*! \brief used to register a FAX technology module with res_fax */
|
||||
struct ast_fax_tech {
|
||||
/*! the type of fax session supported with this ast_fax_tech structure */
|
||||
|
|
|
@ -1484,7 +1484,7 @@ static struct sigaction urg_handler = {
|
|||
|
||||
static void _hup_handler(int num)
|
||||
{
|
||||
int a = 0;
|
||||
int a = 0, save_errno = errno;
|
||||
if (option_verbose > 1)
|
||||
printf("Received HUP signal -- Reloading configs\n");
|
||||
if (restartnow)
|
||||
|
@ -1495,6 +1495,7 @@ static void _hup_handler(int num)
|
|||
fprintf(stderr, "hup_handler: write() failed: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
static struct sigaction hup_handler = {
|
||||
|
@ -1505,7 +1506,7 @@ static struct sigaction hup_handler = {
|
|||
static void _child_handler(int sig)
|
||||
{
|
||||
/* Must not ever ast_log or ast_verbose within signal handler */
|
||||
int n, status;
|
||||
int n, status, save_errno = errno;
|
||||
|
||||
/*
|
||||
* Reap all dead children -- not just one
|
||||
|
@ -1514,6 +1515,7 @@ static void _child_handler(int sig)
|
|||
;
|
||||
if (n == 0 && option_debug)
|
||||
printf("Huh? Child handler, but nobody there?\n");
|
||||
errno = save_errno;
|
||||
}
|
||||
|
||||
static struct sigaction child_handler = {
|
||||
|
|
117
main/bridging.c
117
main/bridging.c
|
@ -50,6 +50,8 @@ static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
|
|||
/* Grow rate of bridge array of channels */
|
||||
#define BRIDGE_ARRAY_GROW 32
|
||||
|
||||
static void cleanup_video_mode(struct ast_bridge *bridge);
|
||||
|
||||
/*! Default DTMF keys for built in features */
|
||||
static char builtin_features_dtmf[AST_BRIDGE_BUILTIN_END][MAXIMUM_DTMF_FEATURE_STRING];
|
||||
|
||||
|
@ -457,6 +459,8 @@ static void destroy_bridge(void *obj)
|
|||
/* Drop the array of channels */
|
||||
ast_free(bridge->array);
|
||||
|
||||
cleanup_video_mode(bridge);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1470,3 +1474,116 @@ void ast_bridge_set_internal_sample_rate(struct ast_bridge *bridge, unsigned int
|
|||
bridge->internal_sample_rate = sample_rate;
|
||||
ao2_unlock(bridge);
|
||||
}
|
||||
|
||||
static void cleanup_video_mode(struct ast_bridge *bridge)
|
||||
{
|
||||
switch (bridge->video_mode.mode) {
|
||||
case AST_BRIDGE_VIDEO_MODE_NONE:
|
||||
break;
|
||||
case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
|
||||
if (bridge->video_mode.mode_data.single_src_data.chan_vsrc) {
|
||||
ast_channel_unref(bridge->video_mode.mode_data.single_src_data.chan_vsrc);
|
||||
}
|
||||
break;
|
||||
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
|
||||
if (bridge->video_mode.mode_data.talker_src_data.chan_vsrc) {
|
||||
ast_channel_unref(bridge->video_mode.mode_data.talker_src_data.chan_vsrc);
|
||||
}
|
||||
}
|
||||
memset(&bridge->video_mode, 0, sizeof(bridge->video_mode));
|
||||
}
|
||||
|
||||
void ast_bridge_set_single_src_video_mode(struct ast_bridge *bridge, struct ast_channel *video_src_chan)
|
||||
{
|
||||
ao2_lock(bridge);
|
||||
cleanup_video_mode(bridge);
|
||||
bridge->video_mode.mode = AST_BRIDGE_VIDEO_MODE_SINGLE_SRC;
|
||||
bridge->video_mode.mode_data.single_src_data.chan_vsrc = ast_channel_ref(video_src_chan);
|
||||
ast_indicate(video_src_chan, AST_CONTROL_VIDUPDATE);
|
||||
ao2_unlock(bridge);
|
||||
}
|
||||
|
||||
void ast_bridge_set_talker_src_video_mode(struct ast_bridge *bridge)
|
||||
{
|
||||
ao2_lock(bridge);
|
||||
cleanup_video_mode(bridge);
|
||||
bridge->video_mode.mode = AST_BRIDGE_VIDEO_MODE_TALKER_SRC;
|
||||
ao2_unlock(bridge);
|
||||
}
|
||||
|
||||
void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct ast_channel *chan, int talker_energy, int is_keyframe)
|
||||
{
|
||||
struct ast_bridge_video_talker_src_data *data;
|
||||
/* If the channel doesn't support video, we don't care about it */
|
||||
if (!ast_format_cap_has_type(chan->nativeformats, AST_FORMAT_TYPE_VIDEO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ao2_lock(bridge);
|
||||
data = &bridge->video_mode.mode_data.talker_src_data;
|
||||
|
||||
if (data->chan_vsrc == chan) {
|
||||
data->average_talking_energy = talker_energy;
|
||||
} else if ((data->average_talking_energy < talker_energy) && is_keyframe) {
|
||||
if (data->chan_vsrc) {
|
||||
ast_channel_unref(data->chan_vsrc);
|
||||
}
|
||||
data->chan_vsrc = ast_channel_ref(chan);
|
||||
data->average_talking_energy = talker_energy;
|
||||
ast_indicate(chan, AST_CONTROL_VIDUPDATE);
|
||||
} else if ((data->average_talking_energy < talker_energy) && !is_keyframe) {
|
||||
ast_indicate(chan, AST_CONTROL_VIDUPDATE);
|
||||
} else if (!data->chan_vsrc && is_keyframe) {
|
||||
data->chan_vsrc = ast_channel_ref(chan);
|
||||
data->average_talking_energy = talker_energy;
|
||||
ast_indicate(chan, AST_CONTROL_VIDUPDATE);
|
||||
}
|
||||
ao2_unlock(bridge);
|
||||
}
|
||||
|
||||
int ast_bridge_is_video_src(struct ast_bridge *bridge, struct ast_channel *chan)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
ao2_lock(bridge);
|
||||
switch (bridge->video_mode.mode) {
|
||||
case AST_BRIDGE_VIDEO_MODE_NONE:
|
||||
break;
|
||||
case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
|
||||
if (bridge->video_mode.mode_data.single_src_data.chan_vsrc == chan) {
|
||||
res = 1;
|
||||
}
|
||||
break;
|
||||
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
|
||||
if (bridge->video_mode.mode_data.talker_src_data.chan_vsrc == chan) {
|
||||
res = 1;
|
||||
}
|
||||
}
|
||||
ao2_unlock(bridge);
|
||||
return res;
|
||||
}
|
||||
|
||||
void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *chan)
|
||||
{
|
||||
ao2_lock(bridge);
|
||||
switch (bridge->video_mode.mode) {
|
||||
case AST_BRIDGE_VIDEO_MODE_NONE:
|
||||
break;
|
||||
case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
|
||||
if (bridge->video_mode.mode_data.single_src_data.chan_vsrc == chan) {
|
||||
if (bridge->video_mode.mode_data.single_src_data.chan_vsrc) {
|
||||
ast_channel_unref(bridge->video_mode.mode_data.single_src_data.chan_vsrc);
|
||||
}
|
||||
bridge->video_mode.mode_data.single_src_data.chan_vsrc = NULL;
|
||||
}
|
||||
break;
|
||||
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
|
||||
if (bridge->video_mode.mode_data.talker_src_data.chan_vsrc == chan) {
|
||||
if (bridge->video_mode.mode_data.talker_src_data.chan_vsrc) {
|
||||
ast_channel_unref(bridge->video_mode.mode_data.talker_src_data.chan_vsrc);
|
||||
}
|
||||
bridge->video_mode.mode_data.talker_src_data.chan_vsrc = NULL;
|
||||
}
|
||||
}
|
||||
ao2_unlock(bridge);
|
||||
}
|
||||
|
|
|
@ -7499,6 +7499,7 @@ enum ast_bridge_result ast_channel_bridge(struct ast_channel *c0, struct ast_cha
|
|||
(c0->tech->bridge == c1->tech->bridge) &&
|
||||
!c0->monitor && !c1->monitor &&
|
||||
!c0->audiohooks && !c1->audiohooks &&
|
||||
ast_framehook_list_is_empty(c0->framehooks) && ast_framehook_list_is_empty(c1->framehooks) &&
|
||||
!c0->masq && !c0->masqr && !c1->masq && !c1->masqr) {
|
||||
int timeoutms = to - 1000 > 0 ? to - 1000 : to;
|
||||
/* Looks like they share a bridge method and nothing else is in the way */
|
||||
|
|
29
main/dsp.c
29
main/dsp.c
|
@ -1103,7 +1103,7 @@ int ast_dsp_call_progress(struct ast_dsp *dsp, struct ast_frame *inf)
|
|||
return __ast_dsp_call_progress(dsp, inf->data.ptr, inf->datalen / 2);
|
||||
}
|
||||
|
||||
static int __ast_dsp_silence_noise(struct ast_dsp *dsp, short *s, int len, int *totalsilence, int *totalnoise)
|
||||
static int __ast_dsp_silence_noise(struct ast_dsp *dsp, short *s, int len, int *totalsilence, int *totalnoise, int *frames_energy)
|
||||
{
|
||||
int accum;
|
||||
int x;
|
||||
|
@ -1163,6 +1163,9 @@ static int __ast_dsp_silence_noise(struct ast_dsp *dsp, short *s, int len, int *
|
|||
if (totalnoise) {
|
||||
*totalnoise = dsp->totalnoise;
|
||||
}
|
||||
if (frames_energy) {
|
||||
*frames_energy = accum;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1318,7 +1321,25 @@ int ast_dsp_silence(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence)
|
|||
}
|
||||
s = f->data.ptr;
|
||||
len = f->datalen/2;
|
||||
return __ast_dsp_silence_noise(dsp, s, len, totalsilence, NULL);
|
||||
return __ast_dsp_silence_noise(dsp, s, len, totalsilence, NULL, NULL);
|
||||
}
|
||||
|
||||
int ast_dsp_silence_with_energy(struct ast_dsp *dsp, struct ast_frame *f, int *totalsilence, int *frames_energy)
|
||||
{
|
||||
short *s;
|
||||
int len;
|
||||
|
||||
if (f->frametype != AST_FRAME_VOICE) {
|
||||
ast_log(LOG_WARNING, "Can't calculate silence on a non-voice frame\n");
|
||||
return 0;
|
||||
}
|
||||
if (!ast_format_is_slinear(&f->subclass.format)) {
|
||||
ast_log(LOG_WARNING, "Can only calculate silence on signed-linear frames :(\n");
|
||||
return 0;
|
||||
}
|
||||
s = f->data.ptr;
|
||||
len = f->datalen/2;
|
||||
return __ast_dsp_silence_noise(dsp, s, len, totalsilence, NULL, frames_energy);
|
||||
}
|
||||
|
||||
int ast_dsp_noise(struct ast_dsp *dsp, struct ast_frame *f, int *totalnoise)
|
||||
|
@ -1336,7 +1357,7 @@ int ast_dsp_noise(struct ast_dsp *dsp, struct ast_frame *f, int *totalnoise)
|
|||
}
|
||||
s = f->data.ptr;
|
||||
len = f->datalen/2;
|
||||
return __ast_dsp_silence_noise(dsp, s, len, NULL, totalnoise);
|
||||
return __ast_dsp_silence_noise(dsp, s, len, NULL, totalnoise, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1393,7 +1414,7 @@ struct ast_frame *ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp,
|
|||
|
||||
/* Need to run the silence detection stuff for silence suppression and busy detection */
|
||||
if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) || (dsp->features & DSP_FEATURE_BUSY_DETECT)) {
|
||||
res = __ast_dsp_silence_noise(dsp, shortdata, len, &silence, NULL);
|
||||
res = __ast_dsp_silence_noise(dsp, shortdata, len, &silence, NULL, NULL);
|
||||
}
|
||||
|
||||
if ((dsp->features & DSP_FEATURE_SILENCE_SUPPRESS) && silence) {
|
||||
|
|
|
@ -3740,10 +3740,21 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast
|
|||
break;
|
||||
case AST_CONTROL_OPTION:
|
||||
aoh = f->data.ptr;
|
||||
/* Forward option Requests */
|
||||
/* Forward option Requests, but only ones we know are safe
|
||||
* These are ONLY sent by chan_iax2 and I'm not convinced that
|
||||
* they are useful. I haven't deleted them entirely because I
|
||||
* just am not sure of the ramifications of removing them. */
|
||||
if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
|
||||
ast_channel_setoption(other, ntohs(aoh->option), aoh->data,
|
||||
f->datalen - sizeof(struct ast_option_header), 0);
|
||||
switch (ntohs(aoh->option)) {
|
||||
case AST_OPTION_TONE_VERIFY:
|
||||
case AST_OPTION_TDD:
|
||||
case AST_OPTION_RELAXDTMF:
|
||||
case AST_OPTION_AUDIO_MODE:
|
||||
case AST_OPTION_DIGIT_DETECT:
|
||||
case AST_OPTION_FAX_DETECT:
|
||||
ast_channel_setoption(other, ntohs(aoh->option), aoh->data,
|
||||
f->datalen - sizeof(struct ast_option_header), 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
11
main/file.c
11
main/file.c
|
@ -29,6 +29,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "asterisk/_private.h" /* declare ast_file_init() */
|
||||
|
@ -289,6 +290,8 @@ static int exts_compare(const char *exts, const char *type)
|
|||
static void filestream_destructor(void *arg)
|
||||
{
|
||||
struct ast_filestream *f = arg;
|
||||
int status;
|
||||
int pid = -1;
|
||||
|
||||
/* Stop a running stream if there is one */
|
||||
if (f->owner) {
|
||||
|
@ -306,8 +309,14 @@ static void filestream_destructor(void *arg)
|
|||
ast_translator_free_path(f->trans);
|
||||
|
||||
if (f->realfilename && f->filename) {
|
||||
if (ast_safe_fork(0) == 0) {
|
||||
pid = ast_safe_fork(0);
|
||||
if (!pid) {
|
||||
execl("/bin/mv", "mv", "-f", f->filename, f->realfilename, SENTINEL);
|
||||
_exit(1);
|
||||
}
|
||||
else if (pid > 0) {
|
||||
/* Block the parent until the move is complete.*/
|
||||
waitpid(pid, &status, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -381,7 +381,7 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr)
|
|||
out->samples = fr->samples;
|
||||
out->offset = fr->offset;
|
||||
/* Copy the timing data */
|
||||
ast_copy_flags(out, fr, AST_FRFLAG_HAS_TIMING_INFO);
|
||||
ast_copy_flags(out, fr, AST_FLAGS_ALL);
|
||||
if (ast_test_flag(fr, AST_FRFLAG_HAS_TIMING_INFO)) {
|
||||
out->ts = fr->ts;
|
||||
out->len = fr->len;
|
||||
|
@ -505,7 +505,7 @@ struct ast_frame *ast_frdup(const struct ast_frame *f)
|
|||
/* Must have space since we allocated for it */
|
||||
strcpy(src, f->src);
|
||||
}
|
||||
ast_copy_flags(out, f, AST_FRFLAG_HAS_TIMING_INFO);
|
||||
ast_copy_flags(out, f, AST_FLAGS_ALL);
|
||||
out->ts = f->ts;
|
||||
out->len = f->len;
|
||||
out->seqno = f->seqno;
|
||||
|
|
162
main/manager.c
162
main/manager.c
|
@ -134,6 +134,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
<parameter name="ActionID">
|
||||
<para>ActionID for this transaction. Will be returned.</para>
|
||||
</parameter>
|
||||
<parameter name="Username" required="true">
|
||||
<para>Username to login with as specified in manager.conf.</para>
|
||||
</parameter>
|
||||
<parameter name="Secret">
|
||||
<para>Secret to login with as specified in manager.conf.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Login Manager.</para>
|
||||
|
@ -806,6 +812,52 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
<para>Generates an AOC-D or AOC-E message on a channel.</para>
|
||||
</description>
|
||||
</manager>
|
||||
<manager name="Filter" language="en_US">
|
||||
<synopsis>
|
||||
Dynamically add filters for the current manager session.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<parameter name="Operation">
|
||||
<enumlist>
|
||||
<enum name="Add">
|
||||
<para>Add a filter.</para>
|
||||
</enum>
|
||||
</enumlist>
|
||||
</parameter>
|
||||
<parameter name="Filter">
|
||||
<para>Filters can be whitelist or blacklist</para>
|
||||
<para>Example whitelist filter: "Event: Newchannel"</para>
|
||||
<para>Example blacklist filter: "!Channel: DAHDI.*"</para>
|
||||
<para>This filter option is used to whitelist or blacklist events per user to be
|
||||
reported with regular expressions and are allowed if both the regex matches
|
||||
and the user has read access as defined in manager.conf. Filters are assumed to be for whitelisting
|
||||
unless preceeded by an exclamation point, which marks it as being black.
|
||||
Evaluation of the filters is as follows:</para>
|
||||
<para>- If no filters are configured all events are reported as normal.</para>
|
||||
<para>- If there are white filters only: implied black all filter processed first, then white filters.</para>
|
||||
<para>- If there are black filters only: implied white all filter processed first, then black filters.</para>
|
||||
<para>- If there are both white and black filters: implied black all filter processed first, then white
|
||||
filters, and lastly black filters.</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>The filters added are only used for the current session.
|
||||
Once the connection is closed the filters are removed.</para>
|
||||
<para>This comand requires the system permission because
|
||||
this command can be used to create filters that may bypass
|
||||
filters defined in manager.conf</para>
|
||||
</description>
|
||||
</manager>
|
||||
<manager name="FilterList" language="en_US">
|
||||
<synopsis>
|
||||
Show current event filters for this session
|
||||
</synopsis>
|
||||
<description>
|
||||
<para>The filters displayed are for the current session. Only those filters defined in
|
||||
manager.conf will be present upon starting a new session.</para>
|
||||
</description>
|
||||
</manager>
|
||||
***/
|
||||
|
||||
enum error_type {
|
||||
|
@ -822,6 +874,11 @@ enum error_type {
|
|||
FAILURE_APPEND
|
||||
};
|
||||
|
||||
enum add_filter_result {
|
||||
FILTER_SUCCESS,
|
||||
FILTER_ALLOC_FAILED,
|
||||
FILTER_COMPILE_FAIL,
|
||||
};
|
||||
|
||||
/*!
|
||||
* Linked list of events.
|
||||
|
@ -1013,6 +1070,8 @@ static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
|
|||
|
||||
static void free_channelvars(void);
|
||||
|
||||
static enum add_filter_result manager_add_filter(const char *filter_pattern, struct ao2_container *whitefilters, struct ao2_container *blackfilters);
|
||||
|
||||
/*! \brief Add a custom hook to be called when an event is fired */
|
||||
void ast_manager_register_hook(struct manager_custom_hook *hook)
|
||||
{
|
||||
|
@ -4116,6 +4175,88 @@ static int blackfilter_cmp_fn(void *obj, void *arg, void *data, int flags)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* \brief Manager command to add an event filter to a manager session
|
||||
* \see For more details look at manager_add_filter
|
||||
*/
|
||||
static int action_filter(struct mansession *s, const struct message *m)
|
||||
{
|
||||
const char *filter = astman_get_header(m, "Filter");
|
||||
const char *operation = astman_get_header(m, "Operation");
|
||||
int res;
|
||||
|
||||
if (!strcasecmp(operation, "Add")) {
|
||||
res = manager_add_filter(filter, s->session->whitefilters, s->session->blackfilters);
|
||||
|
||||
if (res != FILTER_SUCCESS) {
|
||||
if (res == FILTER_ALLOC_FAILED) {
|
||||
astman_send_error(s, m, "Internal Error. Failed to allocate regex for filter");
|
||||
return 0;
|
||||
} else if (res == FILTER_COMPILE_FAIL) {
|
||||
astman_send_error(s, m, "Filter did not compile. Check the syntax of the filter given.");
|
||||
return 0;
|
||||
} else {
|
||||
astman_send_error(s, m, "Internal Error. Failed adding filter.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
astman_send_ack(s, m, "Success");
|
||||
return 0;
|
||||
}
|
||||
|
||||
astman_send_error(s, m, "Unknown operation");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* \brief Add an event filter to a manager session
|
||||
*
|
||||
* \param s manager session to modify filters on
|
||||
* \param filter_pattern Filter syntax to add, see below for syntax
|
||||
*
|
||||
* \return FILTER_ALLOC_FAILED Memory allocation failure
|
||||
* \return FILTER_COMPILE_FAIL If the filter did not compile
|
||||
* \return FILTER_SUCCESS Success
|
||||
*
|
||||
* Filter will be used to match against each line of a manager event
|
||||
* Filter can be any valid regular expression
|
||||
* Filter can be a valid regular expression prefixed with !, which will add the filter as a black filter
|
||||
*
|
||||
* \example filter_pattern = "Event: Newchannel"
|
||||
* \example filter_pattern = "Event: New.*"
|
||||
* \example filter_pattern = "!Channel: DAHDI.*"
|
||||
*
|
||||
*/
|
||||
static enum add_filter_result manager_add_filter(const char *filter_pattern, struct ao2_container *whitefilters, struct ao2_container *blackfilters) {
|
||||
regex_t *new_filter = ao2_t_alloc(sizeof(*new_filter), event_filter_destructor, "event_filter allocation");
|
||||
int is_blackfilter;
|
||||
|
||||
if (!new_filter) {
|
||||
return FILTER_ALLOC_FAILED;
|
||||
}
|
||||
|
||||
if (filter_pattern[0] == '!') {
|
||||
is_blackfilter = 1;
|
||||
filter_pattern++;
|
||||
} else {
|
||||
is_blackfilter = 0;
|
||||
}
|
||||
|
||||
if (regcomp(new_filter, filter_pattern, 0)) {
|
||||
ao2_t_ref(new_filter, -1, "failed to make regx");
|
||||
return FILTER_COMPILE_FAIL;
|
||||
}
|
||||
|
||||
if (is_blackfilter) {
|
||||
ao2_t_link(blackfilters, new_filter, "link new filter into black user container");
|
||||
} else {
|
||||
ao2_t_link(whitefilters, new_filter, "link new filter into white user container");
|
||||
}
|
||||
|
||||
return FILTER_SUCCESS;
|
||||
}
|
||||
|
||||
static int match_filter(struct mansession *s, char *eventdata)
|
||||
{
|
||||
int result = 0;
|
||||
|
@ -6278,6 +6419,7 @@ static int __init_manager(int reload)
|
|||
ast_manager_register_xml("ModuleLoad", EVENT_FLAG_SYSTEM, manager_moduleload);
|
||||
ast_manager_register_xml("ModuleCheck", EVENT_FLAG_SYSTEM, manager_modulecheck);
|
||||
ast_manager_register_xml("AOCMessage", EVENT_FLAG_AOC, action_aocmessage);
|
||||
ast_manager_register_xml("Filter", EVENT_FLAG_SYSTEM, action_filter);
|
||||
|
||||
ast_cli_register_multiple(cli_manager, ARRAY_LEN(cli_manager));
|
||||
ast_extension_state_add(NULL, NULL, manager_state_cb, NULL);
|
||||
|
@ -6557,25 +6699,7 @@ static int __init_manager(int reload)
|
|||
}
|
||||
} else if (!strcasecmp(var->name, "eventfilter")) {
|
||||
const char *value = var->value;
|
||||
regex_t *new_filter = ao2_t_alloc(sizeof(*new_filter), event_filter_destructor, "event_filter allocation");
|
||||
if (new_filter) {
|
||||
int is_blackfilter;
|
||||
if (value[0] == '!') {
|
||||
is_blackfilter = 1;
|
||||
value++;
|
||||
} else {
|
||||
is_blackfilter = 0;
|
||||
}
|
||||
if (regcomp(new_filter, value, 0)) {
|
||||
ao2_t_ref(new_filter, -1, "failed to make regx");
|
||||
} else {
|
||||
if (is_blackfilter) {
|
||||
ao2_t_link(user->blackfilters, new_filter, "link new filter into black user container");
|
||||
} else {
|
||||
ao2_t_link(user->whitefilters, new_filter, "link new filter into white user container");
|
||||
}
|
||||
}
|
||||
}
|
||||
manager_add_filter(value, user->whitefilters, user->blackfilters);
|
||||
} else {
|
||||
ast_debug(1, "%s is an unknown option.\n", var->name);
|
||||
}
|
||||
|
|
|
@ -95,7 +95,14 @@ char *ast_sockaddr_stringify_fmt(const struct ast_sockaddr *sa, int format)
|
|||
return "";
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
if ((format & AST_SOCKADDR_STR_REMOTE) == AST_SOCKADDR_STR_REMOTE) {
|
||||
char *p;
|
||||
if (ast_sockaddr_is_ipv6_link_local(sa) && (p = strchr(host, '%'))) {
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
switch ((format & AST_SOCKADDR_STR_FORMAT_MASK)) {
|
||||
case AST_SOCKADDR_STR_DEFAULT:
|
||||
ast_str_set(&str, 0, sa_tmp->ss.ss_family == AF_INET6 ?
|
||||
"[%s]:%s" : "%s:%s", host, port);
|
||||
|
@ -397,6 +404,12 @@ int ast_sockaddr_is_ipv4_multicast(const struct ast_sockaddr *addr)
|
|||
return ((ast_sockaddr_ipv4(addr) & 0xf0000000) == 0xe0000000);
|
||||
}
|
||||
|
||||
int ast_sockaddr_is_ipv6_link_local(const struct ast_sockaddr *addr)
|
||||
{
|
||||
const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr->ss;
|
||||
return ast_sockaddr_is_ipv6(addr) && IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr);
|
||||
}
|
||||
|
||||
int ast_sockaddr_is_ipv6(const struct ast_sockaddr *addr)
|
||||
{
|
||||
return addr->ss.ss_family == AF_INET6 &&
|
||||
|
|
113
main/pbx.c
113
main/pbx.c
|
@ -252,7 +252,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
<para>If the location that is put into the channel information is bogus, and asterisk cannot
|
||||
find that location in the dialplan, then the execution engine will try to find and execute the code in
|
||||
the <literal>i</literal> (invalid) extension in the current context. If that does not exist, it will try to execute the
|
||||
<literal>h</literal> extension. If either or neither the <literal>h</literal> or <literal>i</literal> extensions
|
||||
<literal>h</literal> extension. If neither the <literal>h</literal> nor <literal>i</literal> extensions
|
||||
have been defined, the channel is hung up, and the execution of instructions on the channel is terminated.
|
||||
What this means is that, for example, you specify a context that does not exist, then
|
||||
it will not be possible to find the <literal>h</literal> or <literal>i</literal> extensions,
|
||||
|
@ -289,7 +289,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
next instruction. If the target location is bogus, and does not exist, the execution engine will try
|
||||
to find and execute the code in the <literal>i</literal> (invalid) extension in the current context.
|
||||
If that does not exist, it will try to execute the <literal>h</literal> extension.
|
||||
If either or neither the <literal>h</literal> or <literal>i</literal> extensions have been defined,
|
||||
If neither the <literal>h</literal> nor <literal>i</literal> extensions have been defined,
|
||||
the channel is hung up, and the execution of instructions on the channel is terminated.
|
||||
Remember that this command can set the current context, and if the context specified
|
||||
does not exist, then it will not be able to find any 'h' or 'i' extensions there, and
|
||||
|
@ -7949,55 +7949,82 @@ int ast_explicit_goto(struct ast_channel *chan, const char *context, const char
|
|||
int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_channel *tmpchan;
|
||||
struct {
|
||||
char *accountcode;
|
||||
char *exten;
|
||||
char *context;
|
||||
char *linkedid;
|
||||
char *name;
|
||||
struct ast_cdr *cdr;
|
||||
int amaflags;
|
||||
int state;
|
||||
struct ast_format readformat;
|
||||
struct ast_format writeformat;
|
||||
} tmpvars = { 0, };
|
||||
|
||||
ast_channel_lock(chan);
|
||||
|
||||
if (chan->pbx) { /* This channel is currently in the PBX */
|
||||
ast_explicit_goto(chan, context, exten, priority + 1);
|
||||
ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
|
||||
} else {
|
||||
/* In order to do it when the channel doesn't really exist within
|
||||
the PBX, we have to make a new channel, masquerade, and start the PBX
|
||||
at the new location */
|
||||
struct ast_channel *tmpchan = ast_channel_alloc(0, chan->_state, 0, 0, chan->accountcode, chan->exten, chan->context, chan->linkedid, chan->amaflags, "AsyncGoto/%s", chan->name);
|
||||
if (!tmpchan) {
|
||||
res = -1;
|
||||
} else {
|
||||
if (chan->cdr) {
|
||||
ast_cdr_discard(tmpchan->cdr);
|
||||
tmpchan->cdr = ast_cdr_dup(chan->cdr); /* share the love */
|
||||
}
|
||||
/* Make formats okay */
|
||||
tmpchan->readformat = chan->readformat;
|
||||
tmpchan->writeformat = chan->writeformat;
|
||||
/* Setup proper location */
|
||||
ast_explicit_goto(tmpchan,
|
||||
S_OR(context, chan->context), S_OR(exten, chan->exten), priority);
|
||||
ast_channel_unlock(chan);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Masquerade into temp channel */
|
||||
if (ast_channel_masquerade(tmpchan, chan)) {
|
||||
/* Failed to set up the masquerade. It's probably chan_local
|
||||
* in the middle of optimizing itself out. Sad. :( */
|
||||
ast_hangup(tmpchan);
|
||||
tmpchan = NULL;
|
||||
res = -1;
|
||||
} else {
|
||||
/* it may appear odd to unlock chan here since the masquerade is on
|
||||
* tmpchan, but no channel locks should be held when doing a masquerade
|
||||
* since a masquerade requires a lock on the channels ao2 container. */
|
||||
ast_channel_unlock(chan);
|
||||
ast_do_masquerade(tmpchan);
|
||||
ast_channel_lock(chan);
|
||||
/* Start the PBX going on our stolen channel */
|
||||
if (ast_pbx_start(tmpchan)) {
|
||||
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
|
||||
ast_hangup(tmpchan);
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
/* In order to do it when the channel doesn't really exist within
|
||||
* the PBX, we have to make a new channel, masquerade, and start the PBX
|
||||
* at the new location */
|
||||
tmpvars.accountcode = ast_strdupa(chan->accountcode);
|
||||
tmpvars.exten = ast_strdupa(chan->exten);
|
||||
tmpvars.context = ast_strdupa(chan->context);
|
||||
tmpvars.linkedid = ast_strdupa(chan->linkedid);
|
||||
tmpvars.name = ast_strdupa(chan->name);
|
||||
tmpvars.amaflags = chan->amaflags;
|
||||
tmpvars.state = chan->_state;
|
||||
ast_format_copy(&tmpvars.writeformat, &chan->writeformat);
|
||||
ast_format_copy(&tmpvars.readformat, &chan->readformat);
|
||||
tmpvars.cdr = chan->cdr ? ast_cdr_dup(chan->cdr) : NULL;
|
||||
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
/* Do not hold any channel locks while calling channel_alloc() since the function
|
||||
* locks the channel container when linking the new channel in. */
|
||||
if (!(tmpchan = ast_channel_alloc(0, tmpvars.state, 0, 0, tmpvars.accountcode, tmpvars.exten, tmpvars.context, tmpvars.linkedid, tmpvars.amaflags, "AsyncGoto/%s", tmpvars.name))) {
|
||||
ast_cdr_discard(tmpvars.cdr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy the cdr info over */
|
||||
if (tmpvars.cdr) {
|
||||
ast_cdr_discard(tmpchan->cdr);
|
||||
tmpchan->cdr = tmpvars.cdr;
|
||||
tmpvars.cdr = NULL;
|
||||
}
|
||||
|
||||
/* Make formats okay */
|
||||
ast_format_copy(&tmpchan->readformat, &tmpvars.readformat);
|
||||
ast_format_copy(&tmpchan->writeformat, &tmpvars.writeformat);
|
||||
|
||||
/* Setup proper location. Never hold another channel lock while calling this function. */
|
||||
ast_explicit_goto(tmpchan, S_OR(context, tmpvars.context), S_OR(exten, tmpvars.exten), priority);
|
||||
|
||||
/* Masquerade into tmp channel */
|
||||
if (ast_channel_masquerade(tmpchan, chan)) {
|
||||
/* Failed to set up the masquerade. It's probably chan_local
|
||||
* in the middle of optimizing itself out. Sad. :( */
|
||||
ast_hangup(tmpchan);
|
||||
tmpchan = NULL;
|
||||
res = -1;
|
||||
} else {
|
||||
ast_do_masquerade(tmpchan);
|
||||
/* Start the PBX going on our stolen channel */
|
||||
if (ast_pbx_start(tmpchan)) {
|
||||
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
|
||||
ast_hangup(tmpchan);
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include "asterisk/translate.h"
|
||||
#include "asterisk/netsock2.h"
|
||||
#include "asterisk/_private.h"
|
||||
#include "asterisk/framehook.h"
|
||||
|
||||
struct ast_srtp_res *res_srtp = NULL;
|
||||
struct ast_srtp_policy_res *res_srtp_policy = NULL;
|
||||
|
@ -853,7 +854,8 @@ static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct a
|
|||
if ((c0->tech_pvt != pvt0) ||
|
||||
(c1->tech_pvt != pvt1) ||
|
||||
(c0->masq || c0->masqr || c1->masq || c1->masqr) ||
|
||||
(c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) {
|
||||
(c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks) ||
|
||||
(!ast_framehook_list_is_empty(c0->framehooks) || !ast_framehook_list_is_empty(c1->framehooks))) {
|
||||
ast_debug(1, "rtp-engine-local-bridge: Oooh, something is weird, backing out\n");
|
||||
/* If a masquerade needs to happen we have to try to read in a frame so that it actually happens. Without this we risk being called again and going into a loop */
|
||||
if ((c0->masq || c0->masqr) && (fr = ast_read(c0))) {
|
||||
|
@ -1046,7 +1048,8 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0,
|
|||
if ((c0->tech_pvt != pvt0) ||
|
||||
(c1->tech_pvt != pvt1) ||
|
||||
(c0->masq || c0->masqr || c1->masq || c1->masqr) ||
|
||||
(c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks)) {
|
||||
(c0->monitor || c0->audiohooks || c1->monitor || c1->audiohooks) ||
|
||||
(!ast_framehook_list_is_empty(c0->framehooks) || !ast_framehook_list_is_empty(c1->framehooks))) {
|
||||
ast_debug(1, "Oooh, something is weird, backing out\n");
|
||||
res = AST_BRIDGE_RETRY;
|
||||
break;
|
||||
|
|
|
@ -1439,10 +1439,16 @@ static int pbx_load_config(const char *config_file)
|
|||
char *tc = NULL;
|
||||
char realext[256] = "";
|
||||
char *stringp, *ext;
|
||||
const char *vfile;
|
||||
|
||||
/* get filename for error reporting from top level or an #include */
|
||||
vfile = !*v->file ? config_file : v->file;
|
||||
|
||||
if (!strncasecmp(v->name, "same", 4)) {
|
||||
if (ast_strlen_zero(lastextension)) {
|
||||
ast_log(LOG_ERROR, "No previous pattern in the first entry of context '%s' to match '%s' at line %d!\n", cxt, v->name, v->lineno);
|
||||
ast_log(LOG_ERROR,
|
||||
"No previous pattern in the first entry of context '%s' to match '%s' at line %d of %s!\n",
|
||||
cxt, v->name, v->lineno, vfile);
|
||||
continue;
|
||||
}
|
||||
if ((stringp = tc = ast_strdup(v->value))) {
|
||||
|
@ -1473,7 +1479,9 @@ process_extension:
|
|||
if ((end = strchr(label, ')'))) {
|
||||
*end = '\0';
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
|
||||
ast_log(LOG_WARNING,
|
||||
"Label missing trailing ')' at line %d of %s\n",
|
||||
v->lineno, vfile);
|
||||
ast_free(tc);
|
||||
continue;
|
||||
}
|
||||
|
@ -1487,7 +1495,9 @@ process_extension:
|
|||
if (lastpri > -2) {
|
||||
ipri = lastpri + 1;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry at line %d!\n", v->lineno);
|
||||
ast_log(LOG_WARNING,
|
||||
"Can't use 'next' priority on the first entry at line %d of %s!\n",
|
||||
v->lineno, vfile);
|
||||
ast_free(tc);
|
||||
continue;
|
||||
}
|
||||
|
@ -1495,18 +1505,23 @@ process_extension:
|
|||
if (lastpri > -2) {
|
||||
ipri = lastpri;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry at line %d!\n", v->lineno);
|
||||
ast_log(LOG_WARNING,
|
||||
"Can't use 'same' priority on the first entry at line %d of %s!\n",
|
||||
v->lineno, vfile);
|
||||
ast_free(tc);
|
||||
continue;
|
||||
}
|
||||
} else if (sscanf(pri, "%30d", &ipri) != 1 &&
|
||||
(ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
|
||||
ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
|
||||
ast_log(LOG_WARNING,
|
||||
"Invalid priority/label '%s' at line %d of %s\n",
|
||||
pri, v->lineno, vfile);
|
||||
ipri = 0;
|
||||
ast_free(tc);
|
||||
continue;
|
||||
} else if (ipri < 1) {
|
||||
ast_log(LOG_WARNING, "Invalid priority '%s' at line %d\n", pri, v->lineno);
|
||||
ast_log(LOG_WARNING, "Invalid priority '%s' at line %d of %s\n",
|
||||
pri, v->lineno, vfile);
|
||||
ast_free(tc);
|
||||
continue;
|
||||
}
|
||||
|
@ -1537,7 +1552,9 @@ process_extension:
|
|||
if ((end = strrchr(data, ')'))) {
|
||||
*end = '\0';
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s' at line %d\n", appl, data, v->lineno);
|
||||
ast_log(LOG_WARNING,
|
||||
"No closing parenthesis found? '%s(%s' at line %d of %s\n",
|
||||
appl, data, v->lineno, vfile);
|
||||
}
|
||||
}
|
||||
ast_free(orig_appl);
|
||||
|
@ -1550,10 +1567,14 @@ process_extension:
|
|||
}
|
||||
lastpri = ipri;
|
||||
if (!ast_opt_dont_warn && (!strcmp(realext, "_.") || !strcmp(realext, "_!"))) {
|
||||
ast_log(LOG_WARNING, "The use of '%s' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X%c' instead at line %d\n", realext, realext[1], v->lineno);
|
||||
ast_log(LOG_WARNING,
|
||||
"The use of '%s' for an extension is strongly discouraged and can have unexpected behavior. Please use '_X%c' instead at line %d of %s\n",
|
||||
realext, realext[1], v->lineno, vfile);
|
||||
}
|
||||
if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free_ptr, registrar)) {
|
||||
ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
|
||||
ast_log(LOG_WARNING,
|
||||
"Unable to register extension at line %d of %s\n",
|
||||
v->lineno, vfile);
|
||||
}
|
||||
}
|
||||
free(tc);
|
||||
|
@ -1561,35 +1582,40 @@ process_extension:
|
|||
pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
|
||||
if (ast_context_add_include2(con, realvalue, registrar)) {
|
||||
switch (errno) {
|
||||
case ENOMEM:
|
||||
ast_log(LOG_WARNING, "Out of memory for context addition\n");
|
||||
break;
|
||||
case ENOMEM:
|
||||
ast_log(LOG_WARNING, "Out of memory for context addition\n");
|
||||
break;
|
||||
|
||||
case EBUSY:
|
||||
ast_log(LOG_WARNING, "Failed to lock context(s) list, please try again later\n");
|
||||
break;
|
||||
case EBUSY:
|
||||
ast_log(LOG_WARNING, "Failed to lock context(s) list, please try again later\n");
|
||||
break;
|
||||
|
||||
case EEXIST:
|
||||
ast_log(LOG_WARNING, "Context '%s' already included in '%s' context on include at line %d\n",
|
||||
v->value, cxt, v->lineno);
|
||||
break;
|
||||
case EEXIST:
|
||||
ast_log(LOG_WARNING,
|
||||
"Context '%s' already included in '%s' context on include at line %d of %s\n",
|
||||
v->value, cxt, v->lineno, vfile);
|
||||
break;
|
||||
|
||||
case ENOENT:
|
||||
case EINVAL:
|
||||
ast_log(LOG_WARNING, "There is no existence of context '%s' included at line %d\n",
|
||||
errno == ENOENT ? v->value : cxt, v->lineno);
|
||||
break;
|
||||
case ENOENT:
|
||||
case EINVAL:
|
||||
ast_log(LOG_WARNING,
|
||||
"There is no existence of context '%s' included at line %d of %s\n",
|
||||
errno == ENOENT ? v->value : cxt, v->lineno, vfile);
|
||||
break;
|
||||
|
||||
default:
|
||||
ast_log(LOG_WARNING, "Failed to include '%s' in '%s' context at line %d\n",
|
||||
v->value, cxt, v->lineno);
|
||||
break;
|
||||
default:
|
||||
ast_log(LOG_WARNING,
|
||||
"Failed to include '%s' in '%s' context at line %d of %s\n",
|
||||
v->value, cxt, v->lineno, vfile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "ignorepat")) {
|
||||
pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
|
||||
if (ast_context_add_ignorepat2(con, realvalue, registrar)) {
|
||||
ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s' at line %d\n", v->value, cxt, v->lineno);
|
||||
ast_log(LOG_WARNING,
|
||||
"Unable to include ignorepat '%s' in context '%s' at line %d of %s\n",
|
||||
v->value, cxt, v->lineno, vfile);
|
||||
}
|
||||
} else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
|
||||
char *stringp = realvalue;
|
||||
|
@ -1603,10 +1629,14 @@ process_extension:
|
|||
appl = strsep(&stringp, "/");
|
||||
data = S_OR(stringp, "");
|
||||
if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), registrar)) {
|
||||
ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s' at line %d\n", v->value, cxt, v->lineno);
|
||||
ast_log(LOG_WARNING,
|
||||
"Unable to include switch '%s' in context '%s' at line %d of %s\n",
|
||||
v->value, cxt, v->lineno, vfile);
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno);
|
||||
ast_log(LOG_WARNING,
|
||||
"==!!== Unknown directive: %s at line %d of %s -- IGNORING!!!\n",
|
||||
v->name, v->lineno, vfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
/*** MODULEINFO
|
||||
<depend>zlib</depend>
|
||||
<use>crypto</use>
|
||||
<use type="external">crypto</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
|
888
res/res_fax.c
888
res/res_fax.c
File diff suppressed because it is too large
Load Diff
|
@ -5,6 +5,22 @@
|
|||
*
|
||||
* Matthew Nicholson <mnicholson@digium.com>
|
||||
*
|
||||
* Initial T.38-gateway code
|
||||
* 2008, Daniel Ferenci <daniel.ferenci@nethemba.com>
|
||||
* Created by Nethemba s.r.o. http://www.nethemba.com
|
||||
* Sponsored by IPEX a.s. http://www.ipex.cz
|
||||
*
|
||||
* T.38-gateway integration into asterisk app_fax and rework
|
||||
* 2008, Gregory Hinton Nietsky <gregory@dnstelecom.co.za>
|
||||
* dns Telecom http://www.dnstelecom.co.za
|
||||
*
|
||||
* Modified to make T.38-gateway compatible with Asterisk 1.6.2
|
||||
* 2010, Anton Verevkin <mymail@verevkin.it>
|
||||
* ViaNetTV http://www.vianettv.com
|
||||
*
|
||||
* Modified to make T.38-gateway work
|
||||
* 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
|
@ -21,6 +37,7 @@
|
|||
* \brief Spandsp T.38 and G.711 FAX Resource
|
||||
*
|
||||
* \author Matthew Nicholson <mnicholson@digium.com>
|
||||
* \author Gregory H. Nietsky <gregory@distrotech.co.za>
|
||||
*
|
||||
* This module registers the Spandsp FAX technology with the res_fax module.
|
||||
*/
|
||||
|
@ -46,9 +63,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||
#include "asterisk/timing.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/res_fax.h"
|
||||
#include "asterisk/channel.h"
|
||||
|
||||
#define SPANDSP_FAX_SAMPLES 160
|
||||
#define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES /* 50 ticks per second, 20ms, 160 samples per second */
|
||||
#define SPANDSP_ENGAGE_UDPTL_NAT_RETRY 3
|
||||
|
||||
static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token);
|
||||
static void spandsp_fax_destroy(struct ast_fax_session *s);
|
||||
|
@ -57,6 +76,9 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
|
|||
static int spandsp_fax_start(struct ast_fax_session *s);
|
||||
static int spandsp_fax_cancel(struct ast_fax_session *s);
|
||||
static int spandsp_fax_switch_to_t38(struct ast_fax_session *s);
|
||||
static int spandsp_fax_gateway_start(struct ast_fax_session *s);
|
||||
static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f);
|
||||
static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s);
|
||||
|
||||
static char *spandsp_fax_cli_show_capabilities(int fd);
|
||||
static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
|
||||
|
@ -75,7 +97,7 @@ static struct ast_fax_tech spandsp_fax_tech = {
|
|||
*/
|
||||
.version = "pre-20090220",
|
||||
#endif
|
||||
.caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE,
|
||||
.caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE | AST_FAX_TECH_GATEWAY,
|
||||
.new_session = spandsp_fax_new,
|
||||
.destroy_session = spandsp_fax_destroy,
|
||||
.read = spandsp_fax_read,
|
||||
|
@ -114,6 +136,7 @@ static struct {
|
|||
struct spandsp_pvt {
|
||||
unsigned int ist38:1;
|
||||
unsigned int isdone:1;
|
||||
enum ast_t38_state ast_t38_state;
|
||||
fax_state_t fax_state;
|
||||
t38_terminal_state_t t38_state;
|
||||
t30_state_t *t30_state;
|
||||
|
@ -121,6 +144,9 @@ struct spandsp_pvt {
|
|||
|
||||
struct spandsp_fax_stats *stats;
|
||||
|
||||
struct spandsp_fax_gw_stats *t38stats;
|
||||
t38_gateway_state_t t38_gw_state;
|
||||
|
||||
struct ast_timer *timer;
|
||||
AST_LIST_HEAD(frame_queue, ast_frame) read_frames;
|
||||
};
|
||||
|
@ -158,7 +184,9 @@ static void session_destroy(struct spandsp_pvt *p)
|
|||
*/
|
||||
static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, const uint8_t *buf, int len, int count)
|
||||
{
|
||||
struct spandsp_pvt *p = data;
|
||||
int res = -1;
|
||||
struct ast_fax_session *s = data;
|
||||
struct spandsp_pvt *p = s->tech_pvt;
|
||||
struct ast_frame fax_frame = {
|
||||
.frametype = AST_FRAME_MODEM,
|
||||
.subclass.integer = AST_MODEM_T38,
|
||||
|
@ -174,13 +202,23 @@ static int t38_tx_packet_handler(t38_core_state_t *t38_core_state, void *data, c
|
|||
AST_FRAME_SET_BUFFER(f, buf, 0, len);
|
||||
|
||||
if (!(f = ast_frisolate(f))) {
|
||||
return -1;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* no need to lock, this all runs in the same thread */
|
||||
AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list);
|
||||
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
|
||||
ast_set_flag(f, AST_FAX_FRFLAG_GATEWAY);
|
||||
if (p->ast_t38_state == T38_STATE_NEGOTIATED) {
|
||||
res = ast_write(s->chan, f);
|
||||
} else {
|
||||
res = ast_queue_frame(s->chan, f);
|
||||
}
|
||||
ast_frfree(f);
|
||||
} else {
|
||||
/* no need to lock, this all runs in the same thread */
|
||||
AST_LIST_INSERT_TAIL(&p->read_frames, f, frame_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
static int update_stats(struct spandsp_pvt *p, int completion_code)
|
||||
|
@ -422,6 +460,11 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke
|
|||
goto e_return;
|
||||
}
|
||||
|
||||
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
|
||||
s->state = AST_FAX_STATE_INITIALIZED;
|
||||
return p;
|
||||
}
|
||||
|
||||
AST_LIST_HEAD_INIT(&p->read_frames);
|
||||
|
||||
if (s->details->caps & AST_FAX_TECH_RECEIVE) {
|
||||
|
@ -450,7 +493,7 @@ static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_toke
|
|||
}
|
||||
|
||||
/* init t38 stuff */
|
||||
t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, p);
|
||||
t38_terminal_init(&p->t38_state, caller_mode, t38_tx_packet_handler, s);
|
||||
set_logging(&p->t38_state.logging, s->details);
|
||||
}
|
||||
|
||||
|
@ -475,7 +518,12 @@ static void spandsp_fax_destroy(struct ast_fax_session *s)
|
|||
{
|
||||
struct spandsp_pvt *p = s->tech_pvt;
|
||||
|
||||
session_destroy(p);
|
||||
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
|
||||
spandsp_fax_gateway_cleanup(s);
|
||||
} else {
|
||||
session_destroy(p);
|
||||
}
|
||||
|
||||
ast_free(p);
|
||||
s->tech_pvt = NULL;
|
||||
s->fd = -1;
|
||||
|
@ -536,6 +584,10 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
|
|||
{
|
||||
struct spandsp_pvt *p = s->tech_pvt;
|
||||
|
||||
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
|
||||
return spandsp_fax_gateway_process(s, f);
|
||||
}
|
||||
|
||||
/* XXX do we need to lock here? */
|
||||
if (s->state == AST_FAX_STATE_COMPLETE) {
|
||||
ast_log(LOG_WARNING, "FAX session '%d' is in the '%s' state.\n", s->id, ast_fax_state_to_str(s->state));
|
||||
|
@ -549,6 +601,182 @@ static int spandsp_fax_write(struct ast_fax_session *s, const struct ast_frame *
|
|||
}
|
||||
}
|
||||
|
||||
/*! \brief generate T.30 packets sent to the T.30 leg of gateway
|
||||
* \param chan T.30 channel
|
||||
* \param data fax session structure
|
||||
* \param len not used
|
||||
* \param samples no of samples generated
|
||||
* \return -1 on failure or 0 on sucess*/
|
||||
static int spandsp_fax_gw_t30_gen(struct ast_channel *chan, void *data, int len, int samples)
|
||||
{
|
||||
int res = -1;
|
||||
struct ast_fax_session *s = data;
|
||||
struct spandsp_pvt *p = s->tech_pvt;
|
||||
uint8_t buffer[AST_FRIENDLY_OFFSET + samples * sizeof(uint16_t)];
|
||||
struct ast_frame *f;
|
||||
struct ast_frame t30_frame = {
|
||||
.frametype = AST_FRAME_VOICE,
|
||||
.src = "res_fax_spandsp_g711",
|
||||
.samples = samples,
|
||||
.flags = AST_FAX_FRFLAG_GATEWAY,
|
||||
};
|
||||
|
||||
AST_FRAME_SET_BUFFER(&t30_frame, buffer, AST_FRIENDLY_OFFSET, t30_frame.samples * sizeof(int16_t));
|
||||
|
||||
ast_format_set(&t30_frame.subclass.format, AST_FORMAT_SLINEAR, 0);
|
||||
if (!(f = ast_frisolate(&t30_frame))) {
|
||||
return p->isdone ? -1 : res;
|
||||
}
|
||||
|
||||
/* generate a T.30 packet */
|
||||
if ((f->samples = t38_gateway_tx(&p->t38_gw_state, f->data.ptr, f->samples))) {
|
||||
f->datalen = f->samples * sizeof(int16_t);
|
||||
res = ast_write(chan, f);
|
||||
}
|
||||
ast_frfree(f);
|
||||
return p->isdone ? -1 : res;
|
||||
}
|
||||
|
||||
/*! \brief simple routine to allocate data to generator
|
||||
* \param chan channel
|
||||
* \param params generator data
|
||||
* \return data to use in generator call*/
|
||||
static void *spandsp_fax_gw_gen_alloc(struct ast_channel *chan, void *params) {
|
||||
ao2_ref(params, +1);
|
||||
return params;
|
||||
}
|
||||
|
||||
static void spandsp_fax_gw_gen_release(struct ast_channel *chan, void *data) {
|
||||
ao2_ref(data, -1);
|
||||
}
|
||||
|
||||
/*! \brief activate a spandsp gateway based on the information in the given fax session
|
||||
* \param s fax session
|
||||
* \return -1 on error 0 on sucess*/
|
||||
static int spandsp_fax_gateway_start(struct ast_fax_session *s) {
|
||||
struct spandsp_pvt *p = s->tech_pvt;
|
||||
struct ast_fax_t38_parameters *t38_param;
|
||||
int i, modems = 0;
|
||||
struct ast_channel *peer;
|
||||
static struct ast_generator t30_gen = {
|
||||
alloc: spandsp_fax_gw_gen_alloc,
|
||||
release: spandsp_fax_gw_gen_release,
|
||||
generate: spandsp_fax_gw_t30_gen,
|
||||
};
|
||||
|
||||
#if SPANDSP_RELEASE_DATE >= 20081012
|
||||
/* for spandsp shaphots 0.0.6 and higher */
|
||||
p->t38_core_state=&p->t38_gw_state.t38x.t38;
|
||||
#else
|
||||
/* for spandsp release 0.0.5 */
|
||||
p->t38_core_state=&p->t38_gw_state.t38;
|
||||
#endif
|
||||
|
||||
if (!t38_gateway_init(&p->t38_gw_state, t38_tx_packet_handler, s)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
p->ist38 = 1;
|
||||
p->ast_t38_state = ast_channel_get_t38_state(s->chan);
|
||||
if (!(peer = ast_bridged_channel(s->chan))) {
|
||||
ast_channel_unlock(s->chan);
|
||||
return -1;
|
||||
}
|
||||
ast_activate_generator(p->ast_t38_state == T38_STATE_NEGOTIATED ? peer : s->chan, &t30_gen , s);
|
||||
|
||||
set_logging(&p->t38_gw_state.logging, s->details);
|
||||
set_logging(&p->t38_core_state->logging, s->details);
|
||||
|
||||
t38_param = (p->ast_t38_state == T38_STATE_NEGOTIATED) ? &s->details->our_t38_parameters : &s->details->their_t38_parameters;
|
||||
t38_set_t38_version(p->t38_core_state, t38_param->version);
|
||||
t38_gateway_set_ecm_capability(&p->t38_gw_state, s->details->option.ecm);
|
||||
t38_set_max_datagram_size(p->t38_core_state, t38_param->max_ifp);
|
||||
t38_set_fill_bit_removal(p->t38_core_state, t38_param->fill_bit_removal);
|
||||
t38_set_mmr_transcoding(p->t38_core_state, t38_param->transcoding_mmr);
|
||||
t38_set_jbig_transcoding(p->t38_core_state, t38_param->transcoding_jbig);
|
||||
t38_set_data_rate_management_method(p->t38_core_state,
|
||||
(t38_param->rate_management == AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF)? 1 : 2);
|
||||
|
||||
t38_gateway_set_transmit_on_idle(&p->t38_gw_state, TRUE);
|
||||
t38_set_sequence_number_handling(p->t38_core_state, TRUE);
|
||||
|
||||
if (AST_FAX_MODEM_V17 & s->details->modems) {
|
||||
modems |= T30_SUPPORT_V17;
|
||||
}
|
||||
if (AST_FAX_MODEM_V27 & s->details->modems) {
|
||||
modems |= T30_SUPPORT_V27TER;
|
||||
}
|
||||
if (AST_FAX_MODEM_V29 & s->details->modems) {
|
||||
modems |= T30_SUPPORT_V29;
|
||||
}
|
||||
if (AST_FAX_MODEM_V34 & s->details->modems) {
|
||||
#if defined(T30_SUPPORT_V34)
|
||||
modems |= T30_SUPPORT_V34;
|
||||
#elif defined(T30_SUPPORT_V34HDX)
|
||||
modems |= T30_SUPPORT_V34HDX;
|
||||
#else
|
||||
ast_log(LOG_WARNING, "v34 not supported in this version of spandsp\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
t38_gateway_set_supported_modems(&p->t38_gw_state, modems);
|
||||
|
||||
/* engage udptl nat on other side of T38 line
|
||||
* (Asterisk changes media ports thus we send a few packets to reinitialize
|
||||
* pinholes in NATs and FWs
|
||||
*/
|
||||
for (i=0; i < SPANDSP_ENGAGE_UDPTL_NAT_RETRY; i++) {
|
||||
#if SPANDSP_RELEASE_DATE >= 20091228
|
||||
t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL);
|
||||
#elif SPANDSP_RELEASE_DATE >= 20081012
|
||||
t38_core_send_indicator(&p->t38_gw_state.t38x.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38x.t38.indicator_tx_count);
|
||||
#else
|
||||
t38_core_send_indicator(&p->t38_gw_state.t38, T38_IND_NO_SIGNAL, p->t38_gw_state.t38.indicator_tx_count);
|
||||
#endif
|
||||
}
|
||||
|
||||
s->state = AST_FAX_STATE_ACTIVE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief process a frame from the bridge
|
||||
* \param s fax session
|
||||
* \param f frame to process
|
||||
* \return 1 on sucess 0 on incorect packet*/
|
||||
static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f)
|
||||
{
|
||||
struct spandsp_pvt *p = s->tech_pvt;
|
||||
|
||||
/*invalid frame*/
|
||||
if (!f->data.ptr || !f->datalen) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Process a IFP packet */
|
||||
if ((f->frametype == AST_FRAME_MODEM) && (f->subclass.integer == AST_MODEM_T38)) {
|
||||
return t38_core_rx_ifp_packet(p->t38_core_state, f->data.ptr, f->datalen, f->seqno);
|
||||
} else if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.format.id == AST_FORMAT_SLINEAR)) {
|
||||
return t38_gateway_rx(&p->t38_gw_state, f->data.ptr, f->samples);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! \brief gather data and clean up after gateway ends
|
||||
* \param s fax session*/
|
||||
static void spandsp_fax_gateway_cleanup(struct ast_fax_session *s)
|
||||
{
|
||||
struct spandsp_pvt *p = s->tech_pvt;
|
||||
t38_stats_t t38_stats;
|
||||
|
||||
t38_gateway_get_transfer_statistics(&p->t38_gw_state, &t38_stats);
|
||||
|
||||
s->details->option.ecm = t38_stats.error_correcting_mode ? AST_FAX_OPTFLAG_TRUE : AST_FAX_OPTFLAG_FALSE;
|
||||
s->details->pages_transferred = t38_stats.pages_transferred;
|
||||
ast_string_field_build(s->details, transfer_rate, "%d", t38_stats.bit_rate);
|
||||
}
|
||||
|
||||
/*! \brief */
|
||||
static int spandsp_fax_start(struct ast_fax_session *s)
|
||||
{
|
||||
|
@ -556,6 +784,10 @@ static int spandsp_fax_start(struct ast_fax_session *s)
|
|||
|
||||
s->state = AST_FAX_STATE_OPEN;
|
||||
|
||||
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
|
||||
return spandsp_fax_gateway_start(s);
|
||||
}
|
||||
|
||||
if (p->ist38) {
|
||||
#if SPANDSP_RELEASE_DATE >= 20080725
|
||||
/* for spandsp shaphots 0.0.6 and higher */
|
||||
|
@ -625,6 +857,12 @@ static int spandsp_fax_start(struct ast_fax_session *s)
|
|||
static int spandsp_fax_cancel(struct ast_fax_session *s)
|
||||
{
|
||||
struct spandsp_pvt *p = s->tech_pvt;
|
||||
|
||||
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
|
||||
p->isdone = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
t30_terminate(p->t30_state);
|
||||
p->isdone = 1;
|
||||
return 0;
|
||||
|
@ -653,7 +891,7 @@ static int spandsp_fax_switch_to_t38(struct ast_fax_session *s)
|
|||
/*! \brief */
|
||||
static char *spandsp_fax_cli_show_capabilities(int fd)
|
||||
{
|
||||
ast_cli(fd, "SEND RECEIVE T.38 G.711\n\n");
|
||||
ast_cli(fd, "SEND RECEIVE T.38 G.711 GATEWAY\n\n");
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -661,35 +899,48 @@ static char *spandsp_fax_cli_show_capabilities(int fd)
|
|||
static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd)
|
||||
{
|
||||
struct spandsp_pvt *p = s->tech_pvt;
|
||||
t30_stats_t stats;
|
||||
|
||||
ao2_lock(s);
|
||||
ast_cli(fd, "%-22s : %d\n", "session", s->id);
|
||||
ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit");
|
||||
ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
|
||||
if (s->state != AST_FAX_STATE_UNINITIALIZED) {
|
||||
t30_get_transfer_statistics(p->t30_state, &stats);
|
||||
ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status));
|
||||
ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
|
||||
ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
|
||||
ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution);
|
||||
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
|
||||
ast_cli(fd, "%-22s : %d\n", "session", s->id);
|
||||
ast_cli(fd, "%-22s : %s\n", "operation", "Gateway");
|
||||
ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
|
||||
if (s->state != AST_FAX_STATE_UNINITIALIZED) {
|
||||
t38_stats_t stats;
|
||||
t38_gateway_get_transfer_statistics(&p->t38_gw_state, &stats);
|
||||
ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
|
||||
ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
|
||||
ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
|
||||
}
|
||||
} else {
|
||||
ast_cli(fd, "%-22s : %d\n", "session", s->id);
|
||||
ast_cli(fd, "%-22s : %s\n", "operation", (s->details->caps & AST_FAX_TECH_RECEIVE) ? "Receive" : "Transmit");
|
||||
ast_cli(fd, "%-22s : %s\n", "state", ast_fax_state_to_str(s->state));
|
||||
if (s->state != AST_FAX_STATE_UNINITIALIZED) {
|
||||
t30_stats_t stats;
|
||||
t30_get_transfer_statistics(p->t30_state, &stats);
|
||||
ast_cli(fd, "%-22s : %s\n", "Last Status", t30_completion_code_to_str(stats.current_status));
|
||||
ast_cli(fd, "%-22s : %s\n", "ECM Mode", stats.error_correcting_mode ? "Yes" : "No");
|
||||
ast_cli(fd, "%-22s : %d\n", "Data Rate", stats.bit_rate);
|
||||
ast_cli(fd, "%-22s : %dx%d\n", "Image Resolution", stats.x_resolution, stats.y_resolution);
|
||||
#if SPANDSP_RELEASE_DATE >= 20090220
|
||||
ast_cli(fd, "%-22s : %d\n", "Page Number", ((s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1);
|
||||
ast_cli(fd, "%-22s : %d\n", "Page Number", ((s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1);
|
||||
#else
|
||||
ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
|
||||
ast_cli(fd, "%-22s : %d\n", "Page Number", stats.pages_transferred + 1);
|
||||
#endif
|
||||
ast_cli(fd, "%-22s : %s\n", "File Name", s->details->caps & AST_FAX_TECH_RECEIVE ? p->t30_state->rx_file : p->t30_state->tx_file);
|
||||
ast_cli(fd, "%-22s : %s\n", "File Name", s->details->caps & AST_FAX_TECH_RECEIVE ? p->t30_state->rx_file : p->t30_state->tx_file);
|
||||
|
||||
ast_cli(fd, "\nData Statistics:\n");
|
||||
ast_cli(fd, "\nData Statistics:\n");
|
||||
#if SPANDSP_RELEASE_DATE >= 20090220
|
||||
ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx);
|
||||
ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx);
|
||||
ast_cli(fd, "%-22s : %d\n", "Tx Pages", stats.pages_tx);
|
||||
ast_cli(fd, "%-22s : %d\n", "Rx Pages", stats.pages_rx);
|
||||
#else
|
||||
ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
|
||||
ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
|
||||
ast_cli(fd, "%-22s : %d\n", "Tx Pages", (s->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
|
||||
ast_cli(fd, "%-22s : %d\n", "Rx Pages", (s->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
|
||||
#endif
|
||||
ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run);
|
||||
ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows);
|
||||
ast_cli(fd, "%-22s : %d\n", "Longest Bad Line Run", stats.longest_bad_row_run);
|
||||
ast_cli(fd, "%-22s : %d\n", "Total Bad Lines", stats.bad_rows);
|
||||
}
|
||||
}
|
||||
ao2_unlock(s);
|
||||
ast_cli(fd, "\n\n");
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
/*** MODULEINFO
|
||||
<depend>iksemel</depend>
|
||||
<use>openssl</use>
|
||||
<use type="external">openssl</use>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
|
|
@ -1150,7 +1150,9 @@ static void moh_rescan_files(void) {
|
|||
i = ao2_iterator_init(mohclasses, 0);
|
||||
|
||||
while ((c = ao2_iterator_next(&i))) {
|
||||
moh_scan_files(c);
|
||||
if (!strcasecmp(c->mode, "files")) {
|
||||
moh_scan_files(c);
|
||||
}
|
||||
ao2_ref(c, -1);
|
||||
}
|
||||
|
||||
|
@ -1650,14 +1652,15 @@ static int load_moh_classes(int reload)
|
|||
|
||||
cfg = ast_config_load("musiconhold.conf", config_flags);
|
||||
|
||||
if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID) {
|
||||
if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
|
||||
if (ast_check_realtime("musiconhold") && reload) {
|
||||
ao2_t_callback(mohclasses, OBJ_NODATA, moh_class_mark, NULL, "Mark deleted classes");
|
||||
ao2_t_callback(mohclasses, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, moh_classes_delete_marked, NULL, "Purge marked classes");
|
||||
}
|
||||
if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
|
||||
moh_rescan_files();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
|
||||
moh_rescan_files();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -162,7 +162,35 @@ static void timerfd_timer_ack(int handle, unsigned int quantity)
|
|||
uint64_t expirations;
|
||||
int read_result = 0;
|
||||
|
||||
struct timerfd_timer *our_timer, find_helper = {
|
||||
.handle = handle,
|
||||
};
|
||||
|
||||
if (!(our_timer = ao2_find(timerfd_timers, &find_helper, OBJ_POINTER))) {
|
||||
ast_log(LOG_ERROR, "Couldn't find timer with handle %d\n", handle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (our_timer->saved_timer.it_value.tv_nsec == 0L) {
|
||||
ast_log(LOG_DEBUG, "Reading attempt on idle timerfd.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
struct itimerspec timer_status;
|
||||
|
||||
if (timerfd_gettime(handle, &timer_status)) {
|
||||
ast_log(LOG_ERROR, "Call to timerfd_gettime() error: %s\n", strerror(errno));
|
||||
expirations = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((timer_status.it_value.tv_sec == 0) && (timer_status.it_value.tv_nsec == 0)) {
|
||||
ast_log(LOG_DEBUG, "Call to timerfd_timer_ack() with disarmed timer - break now.\n");
|
||||
expirations = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
read_result = read(handle, &expirations, sizeof(expirations));
|
||||
if (read_result == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2011, Digium, Inc.
|
||||
*
|
||||
* Terry Wilson <twilson@digium.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \file
|
||||
* \brief Netsock2 Unit Tests
|
||||
*
|
||||
* \author Terry Wilson <twilson@digium.com>
|
||||
*
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>TEST_FRAMEWORK</depend>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "")
|
||||
|
||||
#include "asterisk/test.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/netsock2.h"
|
||||
#include "asterisk/logger.h"
|
||||
struct parse_test {
|
||||
const char *address;
|
||||
int expected_result;
|
||||
};
|
||||
|
||||
AST_TEST_DEFINE(parsing)
|
||||
{
|
||||
int res = AST_TEST_PASS;
|
||||
struct parse_test test_vals[] = {
|
||||
{ "192.168.1.0", 1 },
|
||||
{ "10.255.255.254", 1 },
|
||||
{ "172.18.5.4", 1 },
|
||||
{ "8.8.4.4", 1 },
|
||||
{ "0.0.0.0", 1 },
|
||||
{ "127.0.0.1", 1 },
|
||||
{ "1.256.3.4", 0 },
|
||||
{ "256.0.0.1", 0 },
|
||||
{ "1.2.3.4:5060", 1 },
|
||||
{ "::ffff:5.6.7.8", 1 },
|
||||
{ "fdf8:f53b:82e4::53", 1 },
|
||||
{ "fe80::200:5aee:feaa:20a2", 1 },
|
||||
{ "2001::1", 1 },
|
||||
{ "2001:0000:4136:e378:8000:63bf:3fff:fdd2", 1 },
|
||||
{ "2001:0002:6c::430", 1 },
|
||||
{ "2001:10:240:ab::a", 1 },
|
||||
{ "2002:cb0a:3cdd:1::1", 1 },
|
||||
{ "2001:db8:8:4::2", 1 }, /* Documentation only, should never be used */
|
||||
{ "ff01:0:0:0:0:0:0:2", 1 }, /* Multicast */
|
||||
{ "[fdf8:f53b:82e4::53]", 1 },
|
||||
{ "[fe80::200:5aee:feaa:20a2]", 1 },
|
||||
{ "[2001::1]", 1 },
|
||||
{ "[2001:0000:4136:e378:8000:63bf:3fff:fdd2]:5060", 1 },
|
||||
{ "2001:0000:4136:e378:8000:63bf:3fff:fdd2:5060", 0 }, /* port, but no brackets */
|
||||
{ "[fe80::200:5aee:feaa:20a2%eth0]", 1 }, /* link-local with scope id */
|
||||
{ "[fe80::200::abcd", 0 }, /* multiple zero expansions */
|
||||
};
|
||||
|
||||
size_t x;
|
||||
struct ast_sockaddr addr = { { 0, 0, } };
|
||||
int parse_result;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "parsing";
|
||||
info->category = "/main/netsock2/";
|
||||
info->summary = "netsock2 parsing unit test";
|
||||
info->description =
|
||||
"Test parsing of IPv4 and IPv6 network addresses";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
for (x = 0; x < ARRAY_LEN(test_vals); x++) {
|
||||
if ((parse_result = ast_sockaddr_parse(&addr, test_vals[x].address, 0)) != test_vals[x].expected_result) {
|
||||
ast_test_status_update(test, "On '%s' expected %d but got %d\n", test_vals[x].address, test_vals[x].expected_result, parse_result);
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
if (parse_result) {
|
||||
struct ast_sockaddr tmp_addr = { { 0, 0, } };
|
||||
const char *tmp;
|
||||
|
||||
tmp = ast_sockaddr_stringify(&addr);
|
||||
ast_sockaddr_parse(&tmp_addr, tmp, 0);
|
||||
if (ast_sockaddr_cmp_addr(&addr, &tmp_addr)) {
|
||||
ast_test_status_update(test, "Re-parsed stringification did not match: '%s' vs '%s'\n", ast_sockaddr_stringify(&addr), ast_sockaddr_stringify(&tmp_addr));
|
||||
res = AST_TEST_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
AST_TEST_UNREGISTER(parsing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
AST_TEST_REGISTER(parsing);
|
||||
return AST_MODULE_LOAD_SUCCESS;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Netsock2 test module");
|
Reference in New Issue