/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2014, Anthony Minessale II * * 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/ * * 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 * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Anthony Minessale II * Neal Horman * Bret McDanel * Dale Thatcher * Chris Danielson * Rupa Schomaker * David Weekly * Joao Mesquita * Raymond Chandler * Seven Du * Emmanuel Schmidbauer * William King * * mod_conference.c -- Software Conference Bridge * */ #include int conference_member_noise_gate_check(conference_member_t *member) { int r = 0; if (member->conference->agc_level && member->agc_volume_in_level != 0) { int target_score = 0; target_score = (member->energy_level + (25 * member->agc_volume_in_level)); if (target_score < 0) target_score = 0; r = (int)member->score > target_score; } else { r = (int32_t)member->score > member->energy_level; } return r; } void conference_member_clear_avg(conference_member_t *member) { member->avg_score = 0; member->avg_itt = 0; member->avg_tally = 0; member->agc_concur = 0; } 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; switch_ivr_dmachine_bind(member->dmachine, "conf", digits, 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(¶ms, 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) { 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_INFO, "%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; } if (params) switch_event_destroy(¶ms); } 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 || switch_channel_test_flag(member->channel, CF_VIDEO_ONLY) || conference_utils_member_test_flag(member, MFLAG_SECOND_SCREEN)) { return; } switch_live_array_lock(member->conference->la); if (conference_utils_test_flag(member->conference, CFLAG_JSON_STATUS)) { json = cJSON_CreateObject(); audio = cJSON_CreateObject(); cJSON_AddItemToObject(audio, "muted", cJSON_CreateBool(!conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK))); 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 == member->conference->floor_holder)); cJSON_AddItemToObject(audio, "energyScore", cJSON_CreateNumber(member->score)); cJSON_AddItemToObject(json, "audio", audio); if (switch_channel_test_flag(member->channel, CF_VIDEO) || member->avatar_png_img) { video = cJSON_CreateObject(); cJSON_AddItemToObject(video, "avatarPresented", cJSON_CreateBool(!!member->avatar_png_img)); cJSON_AddItemToObject(video, "mediaFlow", cJSON_CreateString(member->video_flow == 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()); } cJSON_AddItemToObject(video, "reservationID", member->video_reservation_id ? cJSON_CreateString(member->video_reservation_id) : cJSON_CreateNull()); cJSON_AddItemToObject(video, "videoLayerID", cJSON_CreateNumber(member->video_layer_id)); cJSON_AddItemToObject(json, "video", video); } else { cJSON_AddItemToObject(json, "video", cJSON_CreateFalse()); } json_display = cJSON_PrintUnformatted(json); cJSON_Delete(json); } else { 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 == 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 (json_display) { member->status_field->valuestring = json_display; } else { free(member->status_field->valuestring); 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 == 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; } #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) { 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_agc_levels(conference_member_t *member) { int x = 0; if (!member->avg_score) return; if ((int)member->avg_score < member->conference->agc_level - 100) { member->agc_volume_in_level++; switch_normalize_volume_granular(member->agc_volume_in_level); x = 1; } else if ((int)member->avg_score > member->conference->agc_level + 100) { member->agc_volume_in_level--; switch_normalize_volume_granular(member->agc_volume_in_level); x = -1; } if (x) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG7, "AGC %s:%d diff:%d level:%d cur:%d avg:%d vol:%d %s\n", member->conference->name, member->id, member->conference->agc_level - member->avg_score, member->conference->agc_level, member->score, member->avg_score, member->agc_volume_in_level, x > 0 ? "+++" : "---"); conference_member_clear_avg(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; } 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; } conference_al_process(member->al, frame->data, frame->datalen, frame->rate); } else { switch_mux_channels((int16_t *) frame->data, rlen, from, to); } frame->datalen = rlen * 2 * to; } } 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; } if (switch_core_speech_read_tts(member->fnode->sh, file_frame, &speech_len, &flags) == SWITCH_STATUS_SUCCESS) { 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 (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; } /* 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) { /* 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_assert(conference != NULL); switch_assert(member != NULL); switch_mutex_lock(conference->mutex); switch_mutex_lock(member->audio_in_mutex); switch_mutex_lock(member->audio_out_mutex); lock_member(member); switch_mutex_lock(conference->member_mutex); if (member->rec) { conference->recording_members++; } member->join_time = switch_epoch_time_now(NULL); member->conference = conference; member->next = conference->members; member->energy_level = conference->energy_level; member->score_iir = 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); if (conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS)) { video_layout_t *vlayout = NULL; switch_mutex_lock(conference->canvas_mutex); if ((vlayout = conference_video_get_layout(conference, conference->video_layout_name, conference->video_layout_group))) { conference_video_init_canvas(conference, vlayout, &member->canvas); conference_video_init_canvas_layers(conference, member->canvas, vlayout); } switch_mutex_unlock(conference->canvas_mutex); } if (member->video_flow == SWITCH_MEDIA_FLOW_SENDONLY) { conference_utils_member_clear_flag_locked(member, MFLAG_CAN_BE_SEEN); } conference->members = member; conference_utils_member_set_flag_locked(member, MFLAG_INTREE); switch_mutex_unlock(conference->member_mutex); conference_cdr_add(member); if (!conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) { 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; } } conference_send_presence(conference); channel = switch_core_session_get_channel(member->session); member->video_flow = switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO); conference_video_check_avatar(member, SWITCH_FALSE); if ((var = switch_channel_get_variable_dup(member->channel, "video_initial_canvas", SWITCH_FALSE, -1))) { int 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))) { int 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 ((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(channel, "video_use_dedicated_encoder")) && switch_true(var)) { conference_utils_member_set_flag_locked(member, MFLAG_NO_MINIMIZE_ENCODING); } 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")) { 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)) || (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)) { conference_file_play(conference, (char *)enter_sound, CONF_DEFAULT_LEADIN, switch_core_session_get_channel(member->session), 0); } 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 { 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)); } 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)); } } 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")) { 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); } } unlock_member(member); switch_mutex_unlock(member->audio_out_mutex); switch_mutex_unlock(member->audio_in_mutex); if (conference->la && member->channel && !switch_channel_test_flag(member->channel, CF_VIDEO_ONLY)) { 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"))); 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); switch_channel_get_variables(member->channel, &var_event); dvars = cJSON_CreateObject(); 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); cJSON_AddItemToArray(member->json, cJSON_CreateNull()); 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); 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); } } if (conference_utils_test_flag(conference, CFLAG_POSITIONAL)) { conference_al_gen_arc(conference, NULL); } conference_event_send_rfc(conference); conference_event_send_json(conference); switch_mutex_unlock(conference->mutex); status = SWITCH_STATUS_SUCCESS; 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) { switch_event_t *event; conference_member_t *old_member = NULL; int old_id = 0; if (conference->floor_holder) { if (conference->floor_holder == member) { return; } else { old_member = conference->floor_holder; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Dropping floor %s\n", switch_channel_get_name(old_member->channel)); } } switch_mutex_lock(conference->mutex); if (member) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Adding floor %s\n", switch_channel_get_name(member->channel)); conference->floor_holder = member; conference_member_update_status_field(member); } else { conference->floor_holder = NULL; } if (old_member) { old_id = old_member->id; conference_member_update_status_field(old_member); old_member->floor_packets = 0; } conference_utils_set_flag(conference, CFLAG_FLOOR_CHANGE); switch_mutex_unlock(conference->mutex); 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(conference->floor_holder, event); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", conference->floor_holder->id); } else { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "New-ID", "none"); } switch_event_fire(&event); } } /* 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->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); } lock_member(member); conference_member_del_relationship(member, 0); conference_cdr_del(member); #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_img_free(&member->avatar_png_img); switch_img_free(&member->video_mute_img); switch_img_free(&member->pcanvas_img); 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); if (member->rec) { conference->recording_members--; } 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_thread_rwlock_unlock(member->rwlock); /* 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 == member->conference->floor_holder) { conference_member_set_floor_holder(member->conference, NULL); } 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--; } 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); conference_event_add_data(conference, 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_channel_test_flag(member->channel, CF_VIDEO_ONLY)) { 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 }; 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 *position = switch_event_get_header(fnode->fh.params, "position"); 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; 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; fp = switch_core_strdup(pool, text); switch_assert(fp); if (!switch_event_create_brackets(fp, '{', '}', ',', ¶ms, &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(¶ms); } 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); } } 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; 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; if (index < 0) { index = 0; } } else { index = cur; if (!strcasecmp(val, "next")) { index++; } else if (!strcasecmp(val, "prev")) { index--; } } if (watching) { if (index > member->conference->canvas_count || !member->conference->canvases[index]) { index = 0; } else if (index < 0) { index = member->conference->canvas_count; } } else { if (index >= member->conference->canvas_count || !member->conference->canvases[index]) { index = 0; } else if (index < 0) { index = member->conference->canvas_count; } } if (index > MAX_CANVASES || index < 0) { return -1; } if (member->conference->canvas_count > 1) { if (index > member->conference->canvas_count) { return -1; } } else { if (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_read_impl(member->session, &member->orig_read_impl); member->native_rate = 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, 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, 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", 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; codec_done1: switch_core_codec_destroy(&member->read_codec); codec_done2: switch_core_codec_destroy(&member->write_codec); done: switch_mutex_unlock(member->audio_out_mutex); return -1; } /* 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: */