1840 lines
60 KiB
C
Raw Normal View History

/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
2015-07-16 17:42:21 -07:00
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
2015-07-16 17:42:21 -07:00
*
* Anthony Minessale II <anthm@freeswitch.org>
* Neal Horman <neal at wanlink dot com>
* Bret McDanel <trixter at 0xdecafbad dot com>
* Dale Thatcher <freeswitch at dalethatcher dot com>
* Chris Danielson <chris at maxpowersoft dot com>
* Rupa Schomaker <rupa@rupa.com>
* David Weekly <david@weekly.org>
* Joao Mesquita <jmesquita@gmail.com>
* Raymond Chandler <intralanman@freeswitch.org>
* Seven Du <dujinfang@gmail.com>
* Emmanuel Schmidbauer <e.schmidbauer@gmail.com>
* William King <william.king@quentustech.com>
*
* mod_conference.c -- Software Conference Bridge
*
*/
#include <mod_conference.h>
int conference_member_noise_gate_check(conference_member_t *member)
{
int r = (int32_t)member->score > member->energy_level;
return r;
}
void conference_member_do_binding(conference_member_t *member, conference_key_callback_t handler, const char *digits, const char *data)
{
key_binding_t *binding;
binding = switch_core_alloc(member->pool, sizeof(*binding));
binding->member = member;
binding->action.binded_dtmf = switch_core_strdup(member->pool, digits);
if (data) {
binding->action.data = switch_core_strdup(member->pool, data);
}
binding->handler = handler;
FS-9325 [mod_dptools] Priority flag for faster bind_digit_action matches Add exec: flag, 'P' (for "Priority") to bind_digit_action so that a match is returned as soon as it is found, without waiting for the inter-digit timeout to expire. This can be very useful where the system needs to be more responsive to the user. By default, if multiple bindings are enabled and one or more use a regex, switch_ivr_dmachine_check_match waits for the inter-digit timeout to expire before returning a match. This ensures overlapping patterns, such as "^\d{4}$" and "^\12{3}$" can both be reliably matched When the 'P' flag is specified with bind_digit_action, whose action is exec, a match is returned as soon as the condition is satisfied, regardless of whether or not a longer match may be possible if further digits were entered. For example: <action application="bind_digit_action" data="test,~^*\d{2}$, exec[P]:execute_extension,myextn,self,self"/> <action application="bind_digit_action" data="test,~^#\d{2}$", exec:execute_extension,myotherextn,self,peer"/> The first example causes a match to be returned immediately after the 2nd digit is received, whereas the second example defaults to waiting for the inter-digit timeout to expire before returning. In cases where the 'P' flag is used with a regex and string, and both are matched, the more explicit, string match will be returned. For example: <action application="bind_digit_action" data="test,~^*\d{2}$, exec[P]:execute_extension,myextn,self,self"/> <action application="bind_digit_action" data="test,*12, exec[P]:execute_extension,myotherextn,self,self"/> If "*12" is matched, myotherextn is executed, because "*12" is more explicit/specific than "^*\d{2}$" If the 'P'(riority) flag is not used, behaviour is unchanged from previous versions. This ensures backward compatibility. FS-9325 #resolve
2016-11-08 13:47:37 +00:00
switch_ivr_dmachine_bind(member->dmachine, "conf", digits, 0, 0, conference_loop_dmachine_dispatcher, binding);
}
void conference_member_bind_controls(conference_member_t *member, const char *controls)
{
switch_xml_t cxml, cfg, xgroups, xcontrol;
switch_event_t *params;
int i;
switch_event_create(&params, SWITCH_EVENT_REQUEST_PARAMS);
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "Conf-Name", member->conference->name);
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "Action", "request-controls");
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "Controls", controls);
if (!(cxml = switch_xml_open_cfg(mod_conference_cf_name, &cfg, params))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", mod_conference_cf_name);
goto end;
}
if (!(xgroups = switch_xml_child(cfg, "caller-controls"))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't find caller-controls in %s\n", mod_conference_cf_name);
goto end;
}
if (!(xgroups = switch_xml_find_child(xgroups, "group", "name", controls))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't find group '%s' in caller-controls section of %s\n", switch_str_nil(controls), mod_conference_cf_name);
goto end;
}
for (xcontrol = switch_xml_child(xgroups, "control"); xcontrol; xcontrol = xcontrol->next) {
2015-07-16 17:42:21 -07:00
const char *key = switch_xml_attr(xcontrol, "action");
const char *digits = switch_xml_attr(xcontrol, "digits");
const char *data = switch_xml_attr_soft(xcontrol, "data");
if (zstr(key) || zstr(digits)) continue;
for(i = 0; i < conference_loop_mapping_len(); i++) {
if (!strcasecmp(key, control_mappings[i].name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s binding '%s' to '%s'\n",
switch_core_session_get_name(member->session), digits, key);
conference_member_do_binding(member, control_mappings[i].handler, digits, data);
}
}
}
end:
/* Release the config registry handle */
if (cxml) {
switch_xml_free(cxml);
cxml = NULL;
}
2015-07-16 17:42:21 -07:00
if (params) switch_event_destroy(&params);
2015-07-16 17:42:21 -07:00
}
void conference_member_update_status_field(conference_member_t *member)
{
char *str, *vstr = "", display[128] = "", *json_display = NULL;
cJSON *json, *audio, *video;
if (!member->conference->la || !member->json || !member->status_field || conference_utils_member_test_flag(member, MFLAG_SECOND_SCREEN)) {
return;
}
switch_live_array_lock(member->conference->la);
if (!conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)) {
str = "MUTE";
} else if (switch_channel_test_flag(member->channel, CF_HOLD)) {
str = "HOLD";
} else if (member->id == member->conference->floor_holder) {
if (conference_utils_member_test_flag(member, MFLAG_TALKING)) {
str = "TALKING (FLOOR)";
} else {
str = "FLOOR";
}
} else if (conference_utils_member_test_flag(member, MFLAG_TALKING)) {
str = "TALKING";
} else {
str = "ACTIVE";
}
if (switch_channel_test_flag(member->channel, CF_VIDEO)) {
if (!conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN)) {
vstr = " VIDEO (BLIND)";
} else {
vstr = " VIDEO";
if (member && member->id == member->conference->video_floor_holder) {
vstr = " VIDEO (FLOOR)";
}
}
}
switch_snprintf(display, sizeof(display), "%s%s", str, vstr);
if (conference_utils_test_flag(member->conference, CFLAG_JSON_STATUS)) {
json = cJSON_CreateObject();
audio = cJSON_CreateObject();
2015-07-16 17:42:21 -07:00
cJSON_AddItemToObject(audio, "muted", cJSON_CreateBool(!conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)));
cJSON_AddItemToObject(audio, "deaf", cJSON_CreateBool(!conference_utils_member_test_flag(member, MFLAG_CAN_HEAR)));
2015-07-16 17:42:21 -07:00
cJSON_AddItemToObject(audio, "onHold", cJSON_CreateBool(switch_channel_test_flag(member->channel, CF_HOLD)));
cJSON_AddItemToObject(audio, "talking", cJSON_CreateBool(conference_utils_member_test_flag(member, MFLAG_TALKING)));
cJSON_AddItemToObject(audio, "floor", cJSON_CreateBool(member->id == member->conference->floor_holder));
cJSON_AddItemToObject(audio, "energyScore", cJSON_CreateNumber(member->score));
cJSON_AddItemToObject(json, "audio", audio);
2015-07-16 17:42:21 -07:00
if (switch_channel_test_flag(member->channel, CF_VIDEO) || member->avatar_png_img) {
video = cJSON_CreateObject();
if (conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN) &&
member->video_layer_id > -1 && switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) != SWITCH_MEDIA_FLOW_SENDONLY) {
cJSON_AddItemToObject(video, "visible", cJSON_CreateTrue());
} else {
cJSON_AddItemToObject(video, "visible", cJSON_CreateFalse());
}
cJSON_AddItemToObject(video, "videoOnly", cJSON_CreateBool(switch_channel_test_flag(member->channel, CF_VIDEO_ONLY)));
if (switch_true(switch_channel_get_variable_dup(member->channel, "video_screen_share", SWITCH_FALSE, -1))) {
cJSON_AddItemToObject(video, "screenShare", cJSON_CreateTrue());
}
cJSON_AddItemToObject(video, "avatarPresented", cJSON_CreateBool(!!member->avatar_png_img));
2015-09-16 12:34:56 -05:00
cJSON_AddItemToObject(video, "mediaFlow", cJSON_CreateString(switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY ? "sendOnly" : "sendRecv"));
cJSON_AddItemToObject(video, "muted", cJSON_CreateBool(!conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN)));
cJSON_AddItemToObject(video, "floor", cJSON_CreateBool(member && member->id == member->conference->video_floor_holder));
if (member && member->id == member->conference->video_floor_holder && conference_utils_test_flag(member->conference, CFLAG_VID_FLOOR_LOCK)) {
cJSON_AddItemToObject(video, "floorLocked", cJSON_CreateTrue());
}
2015-07-16 17:42:21 -07:00
cJSON_AddItemToObject(video, "reservationID", member->video_reservation_id ?
cJSON_CreateString(member->video_reservation_id) : cJSON_CreateNull());
cJSON_AddItemToObject(video, "roleID", member->video_role_id ?
cJSON_CreateString(member->video_role_id) : cJSON_CreateNull());
cJSON_AddItemToObject(video, "videoLayerID", cJSON_CreateNumber(member->video_layer_id));
2015-07-16 17:42:21 -07:00
cJSON_AddItemToObject(json, "video", video);
} else {
cJSON_AddItemToObject(json, "video", cJSON_CreateFalse());
}
if (conference_utils_test_flag(member->conference, CFLAG_JSON_STATUS)) {
cJSON_AddItemToObject(json, "oldStatus", cJSON_CreateString(display));
}
json_display = cJSON_PrintUnformatted(json);
cJSON_Delete(json);
}
switch_safe_free(member->status_field->valuestring);
if (json_display) {
member->status_field->valuestring = json_display;
} else {
member->status_field->valuestring = strdup(display);
}
switch_live_array_add(member->conference->la, switch_core_session_get_uuid(member->session), -1, &member->json, SWITCH_FALSE);
switch_live_array_unlock(member->conference->la);
}
switch_status_t conference_member_add_event_data(conference_member_t *member, switch_event_t *event)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
if (!member)
return status;
if (member->conference) {
status = conference_event_add_data(member->conference, event);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Floor", "%s", (member->id == member->conference->floor_holder) ? "true" : "false" );
}
if (member->session) {
switch_channel_t *channel = switch_core_session_get_channel(member->session);
if (member->verbose_events) {
switch_channel_event_set_data(channel, event);
} else {
switch_channel_event_set_basic_data(channel, event);
}
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Video", "%s",
switch_channel_test_flag(switch_core_session_get_channel(member->session), CF_VIDEO) ? "true" : "false" );
}
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Hear", "%s", conference_utils_member_test_flag(member, MFLAG_CAN_HEAR) ? "true" : "false" );
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "See", "%s", conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN) ? "true" : "false" );
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Speak", "%s", conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK) ? "true" : "false" );
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Talking", "%s", conference_utils_member_test_flag(member, MFLAG_TALKING) ? "true" : "false" );
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Mute-Detect", "%s", conference_utils_member_test_flag(member, MFLAG_MUTE_DETECT) ? "true" : "false" );
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-Type", "%s", conference_utils_member_test_flag(member, MFLAG_MOD) ? "moderator" : "member");
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-Ghost", "%s", conference_utils_member_test_flag(member, MFLAG_GHOST) ? "true" : "false");
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Energy-Level", "%d", member->energy_level);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Current-Energy", "%d", member->score);
return status;
}
#ifndef OPENAL_POSITIONING
switch_status_t conference_member_parse_position(conference_member_t *member, const char *data)
{
return SWITCH_STATUS_FALSE;
}
#else
switch_status_t conference_member_parse_position(conference_member_t *member, const char *data)
{
switch_status_t status = SWITCH_STATUS_FALSE;
if (member->al) {
status = conference_al_parse_position(member->al, data);
}
return status;
2015-07-16 17:42:21 -07:00
}
#endif
/* if other_member has a relationship with member, produce it */
conference_relationship_t *conference_member_get_relationship(conference_member_t *member, conference_member_t *other_member)
{
conference_relationship_t *rel = NULL, *global = NULL;
if (member == NULL || other_member == NULL || member->relationships == NULL)
return NULL;
lock_member(member);
lock_member(other_member);
for (rel = member->relationships; rel; rel = rel->next) {
if (rel->id == other_member->id) {
break;
}
/* 0 matches everyone. (We will still test the others because a real match carries more clout) */
if (rel->id == 0) {
global = rel;
}
}
unlock_member(other_member);
unlock_member(member);
return rel ? rel : global;
}
/* stop playing a file for the member of the conference */
uint32_t conference_member_stop_file(conference_member_t *member, file_stop_t stop)
{
conference_file_node_t *nptr;
uint32_t count = 0;
if (member == NULL)
return count;
switch_mutex_lock(member->fnode_mutex);
if (stop == FILE_STOP_ALL) {
for (nptr = member->fnode; nptr; nptr = nptr->next) {
nptr->done++;
count++;
}
} else {
if (member->fnode) {
member->fnode->done++;
count++;
}
}
switch_mutex_unlock(member->fnode_mutex);
return count;
}
/* traverse the conference member list for the specified member id and return it's pointer */
conference_member_t *conference_member_get(conference_obj_t *conference, uint32_t id)
{
conference_member_t *member = NULL;
switch_assert(conference != NULL);
if (!id) {
return NULL;
}
switch_mutex_lock(conference->member_mutex);
for (member = conference->members; member; member = member->next) {
if (conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
continue;
}
if (member->id == id) {
break;
}
}
if (member) {
if (!conference_utils_member_test_flag(member, MFLAG_INTREE) ||
conference_utils_member_test_flag(member, MFLAG_KICKED) ||
(member->session && !switch_channel_up(switch_core_session_get_channel(member->session)))) {
/* member is kicked or hanging up so forget it */
member = NULL;
}
}
if (member) {
if (switch_thread_rwlock_tryrdlock(member->rwlock) != SWITCH_STATUS_SUCCESS) {
/* if you cant readlock it's way to late to do anything */
member = NULL;
}
}
switch_mutex_unlock(conference->member_mutex);
return member;
}
/* traverse the conference member list for the specified member with var/val and return it's pointer */
conference_member_t *conference_member_get_by_var(conference_obj_t *conference, const char *var, const char *val)
{
conference_member_t *member = NULL;
switch_assert(conference != NULL);
if (!(var && val)) {
return NULL;
}
switch_mutex_lock(conference->member_mutex);
for (member = conference->members; member; member = member->next) {
const char *check_var;
if (conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
continue;
}
if ((check_var = switch_channel_get_variable_dup(member->channel, var , SWITCH_FALSE, -1)) && !strcmp(check_var, val)) {
break;
}
}
if (member) {
2015-07-16 17:42:21 -07:00
if (!conference_utils_member_test_flag(member, MFLAG_INTREE) ||
conference_utils_member_test_flag(member, MFLAG_KICKED) ||
(member->session && !switch_channel_up(switch_core_session_get_channel(member->session)))) {
/* member is kicked or hanging up so forget it */
member = NULL;
}
}
if (member) {
if (switch_thread_rwlock_tryrdlock(member->rwlock) != SWITCH_STATUS_SUCCESS) {
/* if you cant readlock it's way to late to do anything */
member = NULL;
}
}
switch_mutex_unlock(conference->member_mutex);
return member;
}
/* traverse the conference member list for the specified member with role and return it's pointer */
conference_member_t *conference_member_get_by_role(conference_obj_t *conference, const char *role_id)
{
conference_member_t *member = NULL;
switch_assert(conference != NULL);
if (zstr(role_id)) {
return NULL;
}
switch_mutex_lock(conference->member_mutex);
for (member = conference->members; member; member = member->next) {
if (conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
continue;
}
if (!zstr(member->video_role_id) && !strcmp(role_id, member->video_role_id)) {
break;
}
}
if (member) {
if (!conference_utils_member_test_flag(member, MFLAG_INTREE) ||
conference_utils_member_test_flag(member, MFLAG_KICKED) ||
(member->session && !switch_channel_up(switch_core_session_get_channel(member->session)))) {
/* member is kicked or hanging up so forget it */
member = NULL;
}
}
if (member) {
if (switch_thread_rwlock_tryrdlock(member->rwlock) != SWITCH_STATUS_SUCCESS) {
/* if you cant readlock it's way to late to do anything */
member = NULL;
}
}
switch_mutex_unlock(conference->member_mutex);
return member;
}
void conference_member_check_channels(switch_frame_t *frame, conference_member_t *member, switch_bool_t in)
{
if (member->conference->channels != member->read_impl.number_of_channels || conference_utils_member_test_flag(member, MFLAG_POSITIONAL)) {
uint32_t rlen;
int from, to;
if (in) {
to = member->conference->channels;
from = member->read_impl.number_of_channels;
} else {
from = member->conference->channels;
to = member->read_impl.number_of_channels;
}
2015-07-16 17:42:21 -07:00
rlen = frame->datalen / 2 / from;
if (in && frame->rate == 48000 && ((from == 1 && to == 2) || (from == 2 && to == 2)) && conference_utils_member_test_flag(member, MFLAG_POSITIONAL)) {
if (from == 2 && to == 2) {
switch_mux_channels((int16_t *) frame->data, rlen, 2, 1);
frame->datalen /= 2;
rlen = frame->datalen / 2;
}
2015-07-16 17:42:21 -07:00
conference_al_process(member->al, frame->data, frame->datalen, frame->rate);
} else {
switch_mux_channels((int16_t *) frame->data, rlen, from, to);
}
2015-07-16 17:42:21 -07:00
frame->datalen = rlen * 2 * to;
2015-07-16 17:42:21 -07:00
}
}
void conference_member_add_file_data(conference_member_t *member, int16_t *data, switch_size_t file_data_len)
{
switch_size_t file_sample_len;
int16_t file_frame[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 };
switch_mutex_lock(member->fnode_mutex);
if (!member->fnode) {
goto done;
}
file_sample_len = file_data_len / 2 / member->conference->channels;
/* if we are done, clean it up */
if (member->fnode->done) {
conference_file_node_t *fnode;
switch_memory_pool_t *pool;
if (member->fnode->type != NODE_TYPE_SPEECH) {
conference_file_close(member->conference, member->fnode);
}
fnode = member->fnode;
member->fnode = member->fnode->next;
pool = fnode->pool;
fnode = NULL;
switch_core_destroy_memory_pool(&pool);
} else if(!switch_test_flag(member->fnode, NFLAG_PAUSE)) {
/* skip this frame until leadin time has expired */
if (member->fnode->leadin) {
member->fnode->leadin--;
} else {
if (member->fnode->type == NODE_TYPE_SPEECH) {
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_BLOCKING;
switch_size_t speech_len = file_data_len;
if (member->fnode->al) {
speech_len /= 2;
}
2015-07-16 17:42:21 -07:00
if (switch_core_speech_read_tts(member->fnode->sh, file_frame, &speech_len, &flags) == SWITCH_STATUS_SUCCESS) {
2015-07-16 17:42:21 -07:00
file_sample_len = file_data_len / 2 / member->conference->channels;
} else {
file_sample_len = file_data_len = 0;
}
} else if (member->fnode->type == NODE_TYPE_FILE) {
switch_core_file_read(&member->fnode->fh, file_frame, &file_sample_len);
file_data_len = file_sample_len * 2 * member->fnode->fh.channels;
if (member->fnode->fh.vol) {
switch_change_sln_volume_granular((void *)file_frame, (uint32_t)file_sample_len * member->fnode->fh.channels,
member->fnode->fh.vol);
}
}
if (file_sample_len <= 0) {
member->fnode->done++;
} else { /* there is file node data to mix into the frame */
uint32_t i;
int32_t sample;
/* Check for output volume adjustments */
if (member->volume_out_level) {
switch_change_sln_volume(file_frame, (uint32_t)file_sample_len * member->conference->channels, member->volume_out_level);
}
if (member->fnode->al) {
conference_al_process(member->fnode->al, file_frame, file_sample_len * 2, member->conference->rate);
}
for (i = 0; i < (int)file_sample_len * member->conference->channels; i++) {
if (member->fnode->mux) {
sample = data[i] + file_frame[i];
switch_normalize_to_16bit(sample);
data[i] = (int16_t)sample;
} else {
data[i] = file_frame[i];
}
}
}
}
}
done:
switch_mutex_unlock(member->fnode_mutex);
}
/* Add a custom relationship to a member */
conference_relationship_t *conference_member_add_relationship(conference_member_t *member, uint32_t id)
{
conference_relationship_t *rel = NULL;
if (member == NULL || id == 0 || !(rel = switch_core_alloc(member->pool, sizeof(*rel))))
return NULL;
rel->id = id;
lock_member(member);
switch_mutex_lock(member->conference->member_mutex);
member->conference->relationship_total++;
switch_mutex_unlock(member->conference->member_mutex);
rel->next = member->relationships;
member->relationships = rel;
unlock_member(member);
return rel;
}
void conference_member_set_score_iir(conference_member_t *member, uint32_t score)
{
member->score_iir = score;
if (member->id == member->conference->floor_holder) {
member->conference->floor_holder_score_iir = score;
}
}
/* Remove a custom relationship from a member */
switch_status_t conference_member_del_relationship(conference_member_t *member, uint32_t id)
{
switch_status_t status = SWITCH_STATUS_FALSE;
conference_relationship_t *rel, *last = NULL;
if (member == NULL)
return status;
lock_member(member);
for (rel = member->relationships; rel; rel = rel->next) {
if (id == 0 || rel->id == id) {
2015-07-16 17:42:21 -07:00
/* we just forget about rel here cos it was allocated by the member's pool
it will be freed when the member is */
conference_member_t *omember;
status = SWITCH_STATUS_SUCCESS;
if (last) {
last->next = rel->next;
} else {
member->relationships = rel->next;
}
if ((rel->flags & RFLAG_CAN_SEND_VIDEO)) {
conference_utils_member_clear_flag(member, MFLAG_RECEIVING_VIDEO);
if ((omember = conference_member_get(member->conference, rel->id))) {
conference_utils_member_clear_flag(omember, MFLAG_RECEIVING_VIDEO);
switch_thread_rwlock_unlock(omember->rwlock);
}
}
switch_mutex_lock(member->conference->member_mutex);
member->conference->relationship_total--;
switch_mutex_unlock(member->conference->member_mutex);
continue;
}
last = rel;
}
unlock_member(member);
return status;
}
/* Gain exclusive access and add the member to the list */
switch_status_t conference_member_add(conference_obj_t *conference, conference_member_t *member)
{
switch_status_t status = SWITCH_STATUS_FALSE;
switch_event_t *event;
char msg[512]; /* conference count announcement */
call_list_t *call_list = NULL;
switch_channel_t *channel;
const char *controls = NULL, *position = NULL, *var = NULL;
switch_bool_t has_video = switch_core_has_video();
switch_assert(conference != NULL);
switch_assert(member != NULL);
switch_mutex_lock(conference->mutex);
if (member->rec) {
conference->recording_members++;
}
member->join_time = switch_epoch_time_now(NULL);
member->conference = conference;
member->energy_level = conference->energy_level;
member->auto_energy_level = conference->auto_energy_level;
member->max_energy_level = conference->max_energy_level;
member->max_energy_hit_trigger = conference->max_energy_hit_trigger;
member->burst_mute_count = conference->burst_mute_count;;
conference_member_set_score_iir(member, 0);
member->verbose_events = conference->verbose_events;
member->video_layer_id = -1;
member->layer_timeout = DEFAULT_LAYER_TIMEOUT;
switch_queue_create(&member->dtmf_queue, 100, member->pool);
conference_utils_member_set_flag_locked(member, MFLAG_INTREE);
conference_cdr_add(member);
if (!conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
2015-09-16 12:34:56 -05:00
conference_api_set_agc(member, NULL);
2015-09-16 12:34:56 -05:00
if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) {
conference_utils_member_clear_flag_locked(member, MFLAG_CAN_BE_SEEN);
}
if (conference_utils_member_test_flag(member, MFLAG_GHOST)) {
conference->count_ghosts++;
} else {
conference->count++;
}
if (conference_utils_member_test_flag(member, MFLAG_ENDCONF)) {
if (conference->end_count++) {
conference->endconference_time = 0;
}
}
channel = switch_core_session_get_channel(member->session);
if (switch_true(switch_channel_get_variable_dup(member->channel, "video_second_screen", SWITCH_FALSE, -1))) {
conference_utils_member_set_flag(member, MFLAG_SECOND_SCREEN);
}
2016-03-18 18:21:25 -05:00
conference_video_check_avatar(member, SWITCH_FALSE);
if ((var = switch_channel_get_variable_dup(member->channel, "video_logo_path", SWITCH_FALSE, -1))) {
conference_member_set_logo(member, var);
}
if ((var = switch_channel_get_variable_dup(member->channel, "video_codec_group", SWITCH_FALSE, -1))) {
member->video_codec_group = switch_core_strdup(member->pool, var);
}
if ((var = switch_channel_get_variable_dup(member->channel, "conference_join_volume_in", SWITCH_FALSE, -1))) {
uint32_t id = atoi(var);
if (id > -5 && id < 5) {
member->volume_in_level = id;
}
}
if ((var = switch_channel_get_variable_dup(member->channel, "conference_join_volume_out", SWITCH_FALSE, -1))) {
uint32_t id = atoi(var);
if (id > -5 && id < 5) {
member->volume_out_level = id;
}
}
if ((var = switch_channel_get_variable_dup(member->channel, "conference_join_energy_level", SWITCH_FALSE, -1))) {
uint32_t id = atoi(var);
if (id > -5 && id < 5) {
member->energy_level = id;
}
}
if ((var = switch_channel_get_variable_dup(member->channel, "video_initial_canvas", SWITCH_FALSE, -1))) {
uint32_t id = atoi(var) - 1;
if (id < conference->canvas_count) {
member->canvas_id = id;
member->layer_timeout = DEFAULT_LAYER_TIMEOUT;
}
}
if ((var = switch_channel_get_variable_dup(member->channel, "video_initial_watching_canvas", SWITCH_FALSE, -1))) {
uint32_t id = atoi(var) - 1;
if (id == 0) {
id = conference->canvas_count;
}
if (id <= conference->canvas_count && conference->canvases[id]) {
member->watching_canvas_id = id;
}
}
conference_video_reset_member_codec_index(member);
if (has_video) {
if ((var = switch_channel_get_variable_dup(member->channel, "video_mute_png", SWITCH_FALSE, -1))) {
member->video_mute_png = switch_core_strdup(member->pool, var);
member->video_mute_img = switch_img_read_png(member->video_mute_png, SWITCH_IMG_FMT_I420);
}
if ((var = switch_channel_get_variable_dup(member->channel, "video_reservation_id", SWITCH_FALSE, -1))) {
member->video_reservation_id = switch_core_strdup(member->pool, var);
}
if ((var = switch_channel_get_variable_dup(member->channel, "video_role_id", SWITCH_FALSE, -1))) {
member->video_role_id = switch_core_strdup(member->pool, var);
}
if ((var = switch_channel_get_variable(channel, "video_use_dedicated_encoder")) && switch_true(var)) {
conference_utils_member_set_flag_locked(member, MFLAG_NO_MINIMIZE_ENCODING);
}
if ((var = switch_channel_get_variable(member->channel, "rtp_video_max_bandwidth_in"))) {
member->max_bw_in = switch_parse_bandwidth_string(var);
}
if ((var = switch_channel_get_variable(member->channel, "rtp_video_max_bandwidth_out"))) {
2016-03-08 12:22:06 +01:00
member->max_bw_out = switch_parse_bandwidth_string(var);
2015-11-12 18:33:32 -06:00
if (member->max_bw_out < conference->video_codec_settings.video.bandwidth) {
conference_utils_member_set_flag_locked(member, MFLAG_NO_MINIMIZE_ENCODING);
switch_core_media_set_outgoing_bitrate(member->session, SWITCH_MEDIA_TYPE_VIDEO, member->max_bw_out);
}
2015-10-03 13:34:09 -05:00
}
}
switch_channel_set_variable_printf(channel, "conference_member_id", "%d", member->id);
switch_channel_set_variable_printf(channel, "conference_moderator", "%s", conference_utils_member_test_flag(member, MFLAG_MOD) ? "true" : "false");
switch_channel_set_variable_printf(channel, "conference_ghost", "%s", conference_utils_member_test_flag(member, MFLAG_GHOST) ? "true" : "false");
switch_channel_set_variable(channel, "conference_recording", conference->record_filename);
switch_channel_set_variable(channel, CONFERENCE_UUID_VARIABLE, conference->uuid_str);
if (switch_channel_test_flag(channel, CF_VIDEO)) {
/* Tell the channel to request a fresh vid frame */
switch_core_session_video_reinit(member->session);
}
if (!switch_channel_get_variable(channel, "conference_call_key")) {
2015-07-16 17:42:21 -07:00
char *key = switch_core_session_sprintf(member->session, "conference_%s_%s_%s",
conference->name, conference->domain, switch_channel_get_variable(channel, "caller_id_number"));
switch_channel_set_variable(channel, "conference_call_key", key);
}
if (conference_utils_test_flag(conference, CFLAG_WAIT_MOD) && conference_utils_member_test_flag(member, MFLAG_MOD)) {
conference_utils_clear_flag(conference, CFLAG_WAIT_MOD);
}
if (conference->count > 1) {
if ((conference->moh_sound && !conference_utils_test_flag(conference, CFLAG_WAIT_MOD)) ||
2015-07-16 17:42:21 -07:00
(conference_utils_test_flag(conference, CFLAG_WAIT_MOD) && !switch_true(switch_channel_get_variable(channel, "conference_permanent_wait_mod_moh")))) {
/* stop MoH if any */
conference_file_stop(conference, FILE_STOP_ASYNC);
}
if (!switch_channel_test_app_flag_key("conference_silent", channel, CONF_SILENT_REQ) && !zstr(conference->enter_sound)) {
const char * enter_sound = switch_channel_get_variable(channel, "conference_enter_sound");
if (conference_utils_test_flag(conference, CFLAG_ENTER_SOUND) && !conference_utils_member_test_flag(member, MFLAG_SILENT)) {
if (!zstr(enter_sound)) {
2015-07-16 17:42:21 -07:00
conference_file_play(conference, (char *)enter_sound, CONF_DEFAULT_LEADIN,
switch_core_session_get_channel(member->session), 0);
2015-07-16 17:42:21 -07:00
} else {
conference_file_play(conference, conference->enter_sound, CONF_DEFAULT_LEADIN, switch_core_session_get_channel(member->session), 0);
}
}
}
}
call_list = (call_list_t *) switch_channel_get_private(channel, "_conference_autocall_list_");
if (call_list) {
char saymsg[1024];
switch_snprintf(saymsg, sizeof(saymsg), "Auto Calling %d parties", call_list->iteration);
conference_member_say(member, saymsg, 0);
} else {
if (!switch_channel_test_app_flag_key("conference_silent", channel, CONF_SILENT_REQ)) {
/* announce the total number of members in the conference */
if (conference->count >= conference->announce_count && conference->announce_count > 1) {
switch_snprintf(msg, sizeof(msg), "There are %d callers", conference->count);
conference_member_say(member, msg, CONF_DEFAULT_LEADIN);
} else if (conference->count == 1 && !conference->perpetual_sound && !conference_utils_test_flag(conference, CFLAG_WAIT_MOD)) {
/* as long as its not a bridge_to conference, announce if person is alone */
if (!conference_utils_test_flag(conference, CFLAG_BRIDGE_TO)) {
if (conference->alone_sound && !conference_utils_member_test_flag(member, MFLAG_GHOST)) {
conference_file_stop(conference, FILE_STOP_ASYNC);
conference_file_play(conference, conference->alone_sound, CONF_DEFAULT_LEADIN,
switch_core_session_get_channel(member->session), 0);
} else {
switch_snprintf(msg, sizeof(msg), "You are currently the only person in this conference.");
conference_member_say(member, msg, CONF_DEFAULT_LEADIN);
}
}
}
}
}
if (conference->min && conference->count >= conference->min) {
conference_utils_set_flag(conference, CFLAG_ENFORCE_MIN);
}
if (!switch_channel_test_app_flag_key("conference_silent", channel, CONF_SILENT_REQ) &&
switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
conference_member_add_event_data(member, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "add-member");
switch_event_fire(&event);
}
switch_channel_clear_app_flag_key("conference_silent", channel, CONF_SILENT_REQ);
switch_channel_set_app_flag_key("conference_silent", channel, CONF_SILENT_DONE);
if ((position = switch_channel_get_variable(channel, "conference_position"))) {
if (conference->channels == 2) {
if (conference_utils_member_test_flag(member, MFLAG_NO_POSITIONAL)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
"%s has positional audio blocked.\n", switch_channel_get_name(channel));
} else {
2015-07-16 17:42:21 -07:00
if (conference_member_parse_position(member, position) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "%s invalid position data\n", switch_channel_get_name(channel));
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s position data set\n", switch_channel_get_name(channel));
}
2015-07-16 17:42:21 -07:00
conference_utils_member_set_flag(member, MFLAG_POSITIONAL);
member->al = conference_al_create(member->pool);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "%s cannot set position data on mono conference.\n", switch_channel_get_name(channel));
}
}
2015-07-16 17:42:21 -07:00
controls = switch_channel_get_variable(channel, "conference_controls");
if (zstr(controls)) {
if (!conference_utils_member_test_flag(member, MFLAG_MOD) || !conference->moderator_controls) {
controls = conference->caller_controls;
} else {
controls = conference->moderator_controls;
}
}
if (zstr(controls)) {
controls = "default";
}
if (strcasecmp(controls, "none")) {
2015-07-16 17:42:21 -07:00
switch_ivr_dmachine_create(&member->dmachine, "mod_conference", NULL,
conference->ivr_dtmf_timeout, conference->ivr_input_timeout, NULL, NULL, NULL);
conference_member_bind_controls(member, controls);
}
2015-07-16 17:42:21 -07:00
}
if (conference->la && member->channel) {
if (!conference_utils_member_test_flag(member, MFLAG_SECOND_SCREEN)) {
cJSON *dvars;
switch_event_t *var_event;
switch_event_header_t *hi;
member->json = cJSON_CreateArray();
cJSON_AddItemToArray(member->json, cJSON_CreateStringPrintf("%0.4d", member->id));
cJSON_AddItemToArray(member->json, cJSON_CreateString(switch_channel_get_variable(member->channel, "caller_id_number")));
cJSON_AddItemToArray(member->json, cJSON_CreateString(switch_channel_get_variable(member->channel, "caller_id_name")));
2015-07-16 17:42:21 -07:00
cJSON_AddItemToArray(member->json, cJSON_CreateStringPrintf("%s@%s",
switch_channel_get_variable(member->channel, "original_read_codec"),
switch_channel_get_variable(member->channel, "original_read_rate")
));
member->status_field = cJSON_CreateString("");
cJSON_AddItemToArray(member->json, member->status_field);
if (conference_utils_test_flag(member->conference, CFLAG_JSON_STATUS)) {
switch_channel_get_variables(member->channel, &var_event);
dvars = cJSON_CreateObject();
2015-07-16 17:42:21 -07:00
for (hi = var_event->headers; hi; hi = hi->next) {
if (!strncasecmp(hi->name, "verto_dvar_", 11)) {
char *var = hi->name + 11;
if (var) {
cJSON_AddItemToObject(dvars, var, cJSON_CreateString(hi->value));
}
}
}
cJSON_AddItemToArray(member->json, dvars);
switch_event_destroy(&var_event);
}
2015-07-16 17:42:21 -07:00
cJSON_AddItemToArray(member->json, cJSON_CreateNull());
2015-07-16 17:42:21 -07:00
conference_member_update_status_field(member);
//switch_live_array_add_alias(conference->la, switch_core_session_get_uuid(member->session), "conference");
}
conference_event_adv_la(conference, member, SWITCH_TRUE);
2015-07-16 17:42:21 -07:00
if (!conference_utils_member_test_flag(member, MFLAG_SECOND_SCREEN)) {
switch_live_array_add(conference->la, switch_core_session_get_uuid(member->session), -1, &member->json, SWITCH_FALSE);
}
}
switch_mutex_lock(conference->member_mutex);
member->next = conference->members;
conference->members = member;
switch_mutex_unlock(conference->member_mutex);
switch_mutex_unlock(conference->mutex);
status = SWITCH_STATUS_SUCCESS;
conference_send_presence(conference);
if (conference_utils_test_flag(conference, CFLAG_POSITIONAL)) {
conference_al_gen_arc(conference, NULL);
}
conference_event_send_rfc(conference);
conference_event_send_json(conference);
conference_video_find_floor(member, SWITCH_TRUE);
if (conference_utils_member_test_flag(member, MFLAG_JOIN_VID_FLOOR)) {
conference_video_set_floor_holder(conference, member, SWITCH_TRUE);
conference_utils_set_flag(member->conference, CFLAG_VID_FLOOR_LOCK);
if (test_eflag(conference, EFLAG_FLOOR_CHANGE)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s OK video floor %d %s\n",
conference->name, member->id, switch_channel_get_name(member->channel));
}
}
return status;
}
void conference_member_set_floor_holder(conference_obj_t *conference, conference_member_t *member, uint32_t id)
{
switch_event_t *event;
int old_member = 0;
uint32_t old_id = 0;
conference_member_t *lmember = NULL;
if (!member && id) {
member = lmember = conference_member_get(conference, id);
}
if (member && conference_utils_member_test_flag(member, MFLAG_DED_VID_LAYER)) {
goto end;
}
conference->floor_holder_score_iir = 0;
if (conference->floor_holder) {
if ((member && conference->floor_holder == member->id) || (id && conference->floor_holder == id)) {
goto end;
} else {
old_member = conference->floor_holder;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Dropping floor %d\n", old_member);
}
}
if (member) {
2015-07-16 17:42:21 -07:00
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Adding floor %s\n",
switch_channel_get_name(member->channel));
conference->floor_holder = member->id;
conference_member_set_score_iir(member, 0);
conference_member_update_status_field(member);
} else {
conference->floor_holder = 0;
}
if (old_member) {
conference_member_t *omember = NULL;
if ((omember = conference_member_get(conference, old_member))) {
old_id = old_member;
conference_member_update_status_field(omember);
omember->floor_packets = 0;
switch_thread_rwlock_unlock(omember->rwlock);
}
}
conference_utils_set_flag(conference, CFLAG_FLOOR_CHANGE);
if (test_eflag(conference, EFLAG_FLOOR_CHANGE)) {
switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT);
conference_event_add_data(conference, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change");
if (old_id) {
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", old_id);
} else {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Old-ID", "none");
}
if (conference->floor_holder) {
conference_member_add_event_data(member, event);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", conference->floor_holder);
} else {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "New-ID", "none");
}
switch_event_fire(&event);
}
end:
if (lmember) {
switch_thread_rwlock_unlock(lmember->rwlock);
}
}
/* Gain exclusive access and remove the member from the list */
switch_status_t conference_member_del(conference_obj_t *conference, conference_member_t *member)
{
switch_status_t status = SWITCH_STATUS_FALSE;
conference_member_t *imember, *last = NULL;
switch_event_t *event;
conference_file_node_t *member_fnode;
switch_speech_handle_t *member_sh;
const char *exit_sound = NULL;
switch_assert(conference != NULL);
switch_assert(member != NULL);
switch_thread_rwlock_wrlock(member->rwlock);
if (member->video_queue) {
conference_video_flush_queue(member->video_queue, 0);
}
if (member->session && (exit_sound = switch_channel_get_variable(switch_core_session_get_channel(member->session), "conference_exit_sound"))) {
conference_file_play(conference, (char *)exit_sound, CONF_DEFAULT_LEADIN,
switch_core_session_get_channel(member->session), 0);
}
conference_member_set_logo(member, NULL);
lock_member(member);
conference_member_del_relationship(member, 0);
conference_cdr_del(member);
if (member->agc) {
switch_agc_destroy(&member->agc);
}
2015-07-16 17:42:21 -07:00
#ifdef OPENAL_POSITIONING
if (member->al && member->al->device) {
conference_al_close(member->al);
}
#endif
member_fnode = member->fnode;
member_sh = member->sh;
member->fnode = NULL;
member->sh = NULL;
unlock_member(member);
if (member->dmachine) {
switch_ivr_dmachine_destroy(&member->dmachine);
}
member->avatar_patched = 0;
switch_mutex_lock(conference->mutex);
switch_mutex_lock(conference->member_mutex);
switch_mutex_lock(member->audio_in_mutex);
switch_mutex_lock(member->audio_out_mutex);
lock_member(member);
conference_utils_member_clear_flag(member, MFLAG_INTREE);
2016-07-10 22:25:14 -05:00
switch_safe_free(member->text_framedata);
member->text_framesize = 0;
if (member->text_buffer) {
switch_buffer_destroy(&member->text_buffer);
}
if (member->rec) {
conference->recording_members--;
}
2015-07-16 17:42:21 -07:00
for (imember = conference->members; imember; imember = imember->next) {
if (imember == member) {
if (last) {
last->next = imember->next;
} else {
conference->members = imember->next;
}
break;
}
last = imember;
}
switch_mutex_lock(member->flag_mutex);
switch_img_free(&member->avatar_png_img);
switch_img_free(&member->video_mute_img);
switch_img_free(&member->pcanvas_img);
switch_mutex_unlock(member->flag_mutex);
switch_thread_rwlock_unlock(member->rwlock);
2015-07-16 17:42:21 -07:00
/* Close Unused Handles */
if (member_fnode) {
conference_file_node_t *fnode, *cur;
switch_memory_pool_t *pool;
fnode = member_fnode;
while (fnode) {
cur = fnode;
fnode = fnode->next;
if (cur->type != NODE_TYPE_SPEECH) {
conference_file_close(conference, cur);
}
pool = cur->pool;
switch_core_destroy_memory_pool(&pool);
}
}
if (member_sh) {
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
switch_core_speech_close(&member->lsh, &flags);
}
if (member->id == member->conference->floor_holder) {
conference_member_set_floor_holder(member->conference, NULL, 0);
}
if (member->id == member->conference->video_floor_holder) {
conference_utils_clear_flag(member->conference, CFLAG_VID_FLOOR_LOCK);
if (member->conference->last_video_floor_holder) {
member->conference->video_floor_holder = member->conference->last_video_floor_holder;
member->conference->last_video_floor_holder = 0;
}
member->conference->video_floor_holder = 0;
}
if (!conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
switch_channel_t *channel = switch_core_session_get_channel(member->session);
if (conference_utils_member_test_flag(member, MFLAG_GHOST)) {
conference->count_ghosts--;
} else {
conference->count--;
}
conference_video_check_flush(member, SWITCH_FALSE);
if (conference_utils_member_test_flag(member, MFLAG_ENDCONF)) {
if (!--conference->end_count) {
//conference_utils_set_flag_locked(conference, CFLAG_DESTRUCT);
conference->endconference_time = switch_epoch_time_now(NULL);
}
}
conference_send_presence(conference);
switch_channel_set_variable(channel, "conference_call_key", NULL);
if ((conference->min && conference_utils_test_flag(conference, CFLAG_ENFORCE_MIN) && (conference->count + conference->count_ghosts) < conference->min)
|| (conference_utils_test_flag(conference, CFLAG_DYNAMIC) && (conference->count + conference->count_ghosts == 0))) {
conference_utils_set_flag(conference, CFLAG_DESTRUCT);
} else {
if (!switch_true(switch_channel_get_variable(channel, "conference_permanent_wait_mod_moh")) && conference_utils_test_flag(conference, CFLAG_WAIT_MOD)) {
/* Stop MOH if any */
conference_file_stop(conference, FILE_STOP_ASYNC);
}
if (!exit_sound && conference->exit_sound && conference_utils_test_flag(conference, CFLAG_EXIT_SOUND) && !conference_utils_member_test_flag(member, MFLAG_SILENT)) {
conference_file_play(conference, conference->exit_sound, 0, channel, 0);
}
if (conference->count == 1 && conference->alone_sound && !conference_utils_test_flag(conference, CFLAG_WAIT_MOD) && !conference_utils_member_test_flag(member, MFLAG_GHOST)) {
conference_file_stop(conference, FILE_STOP_ASYNC);
conference_file_play(conference, conference->alone_sound, 0, channel, 0);
}
}
if (test_eflag(conference, EFLAG_DEL_MEMBER) &&
switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
conference_member_add_event_data(member, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "del-member");
switch_event_fire(&event);
}
}
conference_video_find_floor(member, SWITCH_FALSE);
conference_video_detach_video_layer(member);
if (member->canvas) {
conference_video_destroy_canvas(&member->canvas);
}
member->conference = NULL;
switch_mutex_unlock(conference->member_mutex);
unlock_member(member);
switch_mutex_unlock(member->audio_out_mutex);
switch_mutex_unlock(member->audio_in_mutex);
if (conference->la && member->session) {
switch_live_array_del(conference->la, switch_core_session_get_uuid(member->session));
//switch_live_array_clear_alias(conference->la, switch_core_session_get_uuid(member->session), "conference");
conference_event_adv_la(conference, member, SWITCH_FALSE);
}
conference_event_send_rfc(conference);
conference_event_send_json(conference);
if (conference_utils_test_flag(conference, CFLAG_POSITIONAL)) {
conference_al_gen_arc(conference, NULL);
}
if (member->session) {
switch_core_media_hard_mute(member->session, SWITCH_FALSE);
}
switch_mutex_unlock(conference->mutex);
status = SWITCH_STATUS_SUCCESS;
return status;
}
void conference_member_send_all_dtmf(conference_member_t *member, conference_obj_t *conference, const char *dtmf)
{
conference_member_t *imember;
switch_mutex_lock(conference->mutex);
switch_mutex_lock(conference->member_mutex);
for (imember = conference->members; imember; imember = imember->next) {
/* don't send to self */
if (imember->id == member->id) {
continue;
}
if (imember->session) {
const char *p;
for (p = dtmf; p && *p; p++) {
switch_dtmf_t *dt, digit = { *p, SWITCH_DEFAULT_DTMF_DURATION };
2015-07-16 17:42:21 -07:00
switch_zmalloc(dt, sizeof(*dt));
*dt = digit;
switch_queue_push(imember->dtmf_queue, dt);
switch_core_session_kill_channel(imember->session, SWITCH_SIG_BREAK);
}
}
}
switch_mutex_unlock(conference->member_mutex);
switch_mutex_unlock(conference->mutex);
}
/* Play a file in the conference room to a member */
switch_status_t conference_member_play_file(conference_member_t *member, char *file, uint32_t leadin, switch_bool_t mux)
{
switch_status_t status = SWITCH_STATUS_FALSE;
char *dfile = NULL, *expanded = NULL;
conference_file_node_t *fnode, *nptr = NULL;
switch_memory_pool_t *pool;
int channels = member->conference->channels;
int bad_params = 0;
if (member == NULL || file == NULL || conference_utils_member_test_flag(member, MFLAG_KICKED))
return status;
if ((expanded = switch_channel_expand_variables(switch_core_session_get_channel(member->session), file)) != file) {
file = expanded;
} else {
expanded = NULL;
}
if (!strncasecmp(file, "say:", 4)) {
if (!zstr(file + 4)) {
status = conference_member_say(member, file + 4, leadin);
}
goto done;
}
if (!switch_is_file_path(file)) {
if (member->conference->sound_prefix) {
if (!(dfile = switch_mprintf("%s%s%s", member->conference->sound_prefix, SWITCH_PATH_SEPARATOR, file))) {
goto done;
}
file = dfile;
} else if (!zstr(file)) {
status = conference_member_say(member, file, leadin);
goto done;
}
}
/* Setup a memory pool to use. */
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_CRIT, "Pool Failure\n");
status = SWITCH_STATUS_MEMERR;
goto done;
}
/* Create a node object */
if (!(fnode = switch_core_alloc(pool, sizeof(*fnode)))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_CRIT, "Alloc Failure\n");
switch_core_destroy_memory_pool(&pool);
status = SWITCH_STATUS_MEMERR;
goto done;
}
fnode->conference = member->conference;
fnode->layer_id = -1;
fnode->type = NODE_TYPE_FILE;
fnode->leadin = leadin;
fnode->mux = mux;
fnode->member_id = member->id;
if (switch_stristr("position=", file)) {
/* positional requires mono input */
fnode->fh.channels = channels = 1;
}
retry:
/* Open the file */
fnode->fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN;
if (switch_core_file_open(&fnode->fh,
file, (uint8_t) channels, member->conference->rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT,
pool) != SWITCH_STATUS_SUCCESS) {
switch_core_destroy_memory_pool(&pool);
status = SWITCH_STATUS_NOTFOUND;
goto done;
}
fnode->pool = pool;
fnode->file = switch_core_strdup(fnode->pool, file);
if (fnode->fh.params) {
const char *vol = switch_event_get_header(fnode->fh.params, "vol");
const char *position = switch_event_get_header(fnode->fh.params, "position");
2015-07-16 17:42:21 -07:00
if (!zstr(vol)) {
fnode->fh.vol = atoi(vol);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "Set playback volume for file: %d\n",fnode->fh.vol);
}
if (!bad_params && !zstr(position) && member->conference->channels == 2) {
fnode->al = conference_al_create(pool);
if (conference_al_parse_position(fnode->al, position) != SWITCH_STATUS_SUCCESS) {
switch_core_file_close(&fnode->fh);
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_ERROR, "Invalid Position Data.\n");
fnode->al = NULL;
channels = member->conference->channels;
bad_params = 1;
goto retry;
}
}
}
/* Queue the node */
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "Queueing file '%s' for play\n", file);
switch_mutex_lock(member->fnode_mutex);
for (nptr = member->fnode; nptr && nptr->next; nptr = nptr->next);
if (nptr) {
nptr->next = fnode;
} else {
member->fnode = fnode;
}
switch_mutex_unlock(member->fnode_mutex);
status = SWITCH_STATUS_SUCCESS;
2015-07-16 17:42:21 -07:00
done:
switch_safe_free(expanded);
switch_safe_free(dfile);
return status;
}
/* Say some thing with TTS in the conference room */
switch_status_t conference_member_say(conference_member_t *member, char *text, uint32_t leadin)
{
conference_obj_t *conference = member->conference;
conference_file_node_t *fnode, *nptr;
switch_memory_pool_t *pool;
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
switch_status_t status = SWITCH_STATUS_FALSE;
char *fp = NULL;
int channels = member->conference->channels;
switch_event_t *params = NULL;
const char *position = NULL;
if (member == NULL || zstr(text))
return SWITCH_STATUS_FALSE;
switch_assert(conference != NULL);
if (!(conference->tts_engine && conference->tts_voice)) {
return SWITCH_STATUS_SUCCESS;
}
/* Setup a memory pool to use. */
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_CRIT, "Pool Failure\n");
return SWITCH_STATUS_MEMERR;
}
/* Create a node object */
if (!(fnode = switch_core_alloc(pool, sizeof(*fnode)))) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_CRIT, "Alloc Failure\n");
switch_core_destroy_memory_pool(&pool);
return SWITCH_STATUS_MEMERR;
}
fnode->conference = conference;
fnode->layer_id = -1;
if (*text == '{') {
char *new_fp;
2015-07-16 17:42:21 -07:00
fp = switch_core_strdup(pool, text);
switch_assert(fp);
if (switch_event_create_brackets(fp, '{', '}', ',', &params, &new_fp, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
new_fp = fp;
}
text = new_fp;
}
fnode->type = NODE_TYPE_SPEECH;
fnode->leadin = leadin;
fnode->pool = pool;
if (params && (position = switch_event_get_header(params, "position"))) {
if (conference->channels != 2) {
position = NULL;
} else {
channels = 1;
fnode->al = conference_al_create(pool);
if (conference_al_parse_position(fnode->al, position) != SWITCH_STATUS_SUCCESS) {
fnode->al = NULL;
channels = conference->channels;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Position Data.\n");
}
}
}
if (member->sh && member->last_speech_channels != channels) {
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
switch_core_speech_close(&member->lsh, &flags);
member->sh = NULL;
}
if (!member->sh) {
memset(&member->lsh, 0, sizeof(member->lsh));
if (switch_core_speech_open(&member->lsh, conference->tts_engine, conference->tts_voice,
conference->rate, conference->interval, channels, &flags, switch_core_session_get_pool(member->session)) !=
SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_ERROR, "Invalid TTS module [%s]!\n", conference->tts_engine);
status = SWITCH_STATUS_FALSE;
goto end;
}
member->last_speech_channels = channels;
member->sh = &member->lsh;
}
/* Queue the node */
switch_mutex_lock(member->fnode_mutex);
for (nptr = member->fnode; nptr && nptr->next; nptr = nptr->next);
if (nptr) {
nptr->next = fnode;
} else {
member->fnode = fnode;
}
fnode->sh = member->sh;
/* Begin Generation */
switch_sleep(200000);
if (*text == '#') {
char *tmp = (char *) text + 1;
char *vp = tmp, voice[128] = "";
if ((tmp = strchr(tmp, '#'))) {
text = tmp + 1;
switch_copy_string(voice, vp, (tmp - vp) + 1);
switch_core_speech_text_param_tts(fnode->sh, "voice", voice);
}
} else {
switch_core_speech_text_param_tts(fnode->sh, "voice", conference->tts_voice);
}
switch_core_speech_feed_tts(fnode->sh, text, &flags);
switch_mutex_unlock(member->fnode_mutex);
status = SWITCH_STATUS_SUCCESS;
end:
if (params) {
switch_event_destroy(&params);
}
return status;
}
/* execute a callback for every member of the conference */
void conference_member_itterator(conference_obj_t *conference, switch_stream_handle_t *stream, uint8_t non_mod, conference_api_member_cmd_t pfncallback, void *data)
{
conference_member_t *member = NULL;
switch_assert(conference != NULL);
switch_assert(stream != NULL);
switch_assert(pfncallback != NULL);
switch_mutex_lock(conference->member_mutex);
for (member = conference->members; member; member = member->next) {
if (!(non_mod && conference_utils_member_test_flag(member, MFLAG_MOD))) {
if (member->session && !conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
pfncallback(member, stream, data);
}
} else {
stream->write_function(stream, "Skipping moderator (member id %d).\n", member->id);
2015-07-16 17:42:21 -07:00
}
}
switch_mutex_unlock(conference->member_mutex);
}
int conference_member_get_canvas_id(conference_member_t *member, const char *val, switch_bool_t watching)
{
int index = -1;
int cur;
2015-07-16 17:42:21 -07:00
if (watching) {
cur = member->watching_canvas_id;
} else {
cur = member->canvas_id;
}
if (!val) {
return -1;
}
if (switch_is_number(val)) {
index = atoi(val) - 1;
2015-07-16 17:42:21 -07:00
if (index < 0) {
index = 0;
}
} else {
index = cur;
if (!strcasecmp(val, "next")) {
index++;
} else if (!strcasecmp(val, "prev")) {
index--;
}
}
2015-07-16 17:42:21 -07:00
if (watching) {
if (index < 0) {
index = member->conference->canvas_count;
} else if ((uint32_t)index > member->conference->canvas_count || !member->conference->canvases[index]) {
index = 0;
}
} else {
if (index < 0) {
index = member->conference->canvas_count;
} else if ((uint32_t)index >= member->conference->canvas_count || !member->conference->canvases[index]) {
index = 0;
}
}
2015-07-16 17:42:21 -07:00
if (index > MAX_CANVASES || index < 0) {
return -1;
}
2015-07-16 17:42:21 -07:00
if (member->conference->canvas_count > 1) {
if ((uint32_t)index > member->conference->canvas_count) {
return -1;
}
} else {
if ((uint32_t)index >= member->conference->canvas_count) {
return -1;
}
}
return index;
}
int conference_member_setup_media(conference_member_t *member, conference_obj_t *conference)
{
switch_codec_implementation_t read_impl = { 0 };
switch_mutex_lock(member->audio_out_mutex);
switch_core_session_get_read_impl(member->session, &read_impl);
if (switch_core_codec_ready(&member->read_codec)) {
switch_core_codec_destroy(&member->read_codec);
memset(&member->read_codec, 0, sizeof(member->read_codec));
}
if (switch_core_codec_ready(&member->write_codec)) {
switch_core_codec_destroy(&member->write_codec);
memset(&member->write_codec, 0, sizeof(member->write_codec));
}
if (member->read_resampler) {
switch_resample_destroy(&member->read_resampler);
}
switch_core_session_get_real_read_impl(member->session, &member->orig_read_impl);
member->native_rate = member->orig_read_impl.samples_per_second;
/* Setup a Signed Linear codec for reading audio. */
if (switch_core_codec_init(&member->read_codec,
"L16",
NULL, NULL, read_impl.actual_samples_per_second, read_impl.microseconds_per_packet / 1000,
2015-07-16 17:42:21 -07:00
read_impl.number_of_channels,
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, member->pool) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG,
"Raw Codec Activation Success L16@%uhz %d channel %dms\n",
read_impl.actual_samples_per_second, read_impl.number_of_channels, read_impl.microseconds_per_packet / 1000);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "Raw Codec Activation Failed L16@%uhz %d channel %dms\n",
read_impl.actual_samples_per_second, read_impl.number_of_channels, read_impl.microseconds_per_packet / 1000);
goto done;
}
if (!member->frame_size) {
member->frame_size = SWITCH_RECOMMENDED_BUFFER_SIZE;
member->frame = switch_core_alloc(member->pool, member->frame_size);
member->mux_frame = switch_core_alloc(member->pool, member->frame_size);
}
if (read_impl.actual_samples_per_second != conference->rate) {
if (switch_resample_create(&member->read_resampler,
read_impl.actual_samples_per_second,
conference->rate, member->frame_size, SWITCH_RESAMPLE_QUALITY, read_impl.number_of_channels) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_CRIT, "Unable to create resampler!\n");
goto done;
}
member->resample_out = switch_core_alloc(member->pool, member->frame_size);
member->resample_out_len = member->frame_size;
/* Setup an audio buffer for the resampled audio */
if (!member->resample_buffer && switch_buffer_create_dynamic(&member->resample_buffer, CONF_DBLOCK_SIZE, CONF_DBUFFER_SIZE, CONF_DBUFFER_MAX)
!= SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_CRIT, "Memory Error Creating Audio Buffer!\n");
goto done;
}
}
/* Setup a Signed Linear codec for writing audio. */
if (switch_core_codec_init(&member->write_codec,
"L16",
NULL,
NULL,
conference->rate,
read_impl.microseconds_per_packet / 1000,
2015-07-16 17:42:21 -07:00
read_impl.number_of_channels,
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, member->pool) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG,
2015-07-16 17:42:21 -07:00
"Raw Codec Activation Success L16@%uhz %d channel %dms\n",
conference->rate, conference->channels, read_impl.microseconds_per_packet / 1000);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "Raw Codec Activation Failed L16@%uhz %d channel %dms\n",
conference->rate, conference->channels, read_impl.microseconds_per_packet / 1000);
goto codec_done2;
}
/* Setup an audio buffer for the incoming audio */
if (!member->audio_buffer && switch_buffer_create_dynamic(&member->audio_buffer, CONF_DBLOCK_SIZE, CONF_DBUFFER_SIZE, CONF_DBUFFER_MAX) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_CRIT, "Memory Error Creating Audio Buffer!\n");
goto codec_done1;
}
/* Setup an audio buffer for the outgoing audio */
if (!member->mux_buffer && switch_buffer_create_dynamic(&member->mux_buffer, CONF_DBLOCK_SIZE, CONF_DBUFFER_SIZE, CONF_DBUFFER_MAX) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_CRIT, "Memory Error Creating Audio Buffer!\n");
goto codec_done1;
}
switch_mutex_unlock(member->audio_out_mutex);
return 0;
2015-07-16 17:42:21 -07:00
codec_done1:
switch_core_codec_destroy(&member->read_codec);
2015-07-16 17:42:21 -07:00
codec_done2:
switch_core_codec_destroy(&member->write_codec);
2015-07-16 17:42:21 -07:00
done:
switch_mutex_unlock(member->audio_out_mutex);
return -1;
}
2015-07-16 17:42:21 -07:00
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/