FS-7845 Refactor of mod_conference to improve compilation speed, and

logical organization.
This commit is contained in:
William King 2015-07-15 09:50:17 -07:00
parent d569b4ab98
commit 756ef77b7b
14 changed files with 12902 additions and 12157 deletions

View File

@ -2,7 +2,8 @@ include $(top_srcdir)/build/modmake.rulesam
MODNAME=mod_conference MODNAME=mod_conference
mod_LTLIBRARIES = mod_conference.la mod_LTLIBRARIES = mod_conference.la
mod_conference_la_SOURCES = mod_conference.c mod_conference_la_SOURCES = mod_conference.c mod_conference_api.c mod_conference_loop.c mod_conference_al.c mod_conference_cdr.c mod_conference_video.c
mod_conference_la_SOURCES += mod_conference_event.c mod_conference_member.c mod_conference_utils.c mod_conference_file.c mod_conference_record.c
mod_conference_la_CFLAGS = $(AM_CFLAGS) -I. mod_conference_la_CFLAGS = $(AM_CFLAGS) -I.
mod_conference_la_LIBADD = $(switch_builddir)/libfreeswitch.la mod_conference_la_LIBADD = $(switch_builddir)/libfreeswitch.la
mod_conference_la_LDFLAGS = -avoid-version -module -no-undefined -shared mod_conference_la_LDFLAGS = -avoid-version -module -no-undefined -shared

View File

@ -0,0 +1,7 @@
runtime in one file, header(with all structures), apps, api commands, utilities
Currently mod_conference can compile with 'time make -j' in:
real 0m9.709s
user 0m9.040s
sys 0m0.568s

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,348 @@
/*
* 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/
*
* 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):
*
* 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>
al_handle_t *create_al(switch_memory_pool_t *pool)
{
al_handle_t *al;
al = switch_core_alloc(pool, sizeof(al_handle_t));
switch_mutex_init(&al->mutex, SWITCH_MUTEX_NESTED, pool);
return al;
}
#ifndef OPENAL_POSITIONING
void gen_arc(conference_obj_t *conference, switch_stream_handle_t *stream)
{
}
void process_al(al_handle_t *al, void *data, switch_size_t datalen, int rate)
{
}
#else
void gen_arc(conference_obj_t *conference, switch_stream_handle_t *stream)
{
float offset;
float pos;
float radius;
float x, z;
float div = 3.14159f / 180;
conference_member_t *member;
uint32_t count = 0;
if (!conference->count) {
return;
}
switch_mutex_lock(conference->member_mutex);
for (member = conference->members; member; member = member->next) {
if (member->channel && member_test_flag(member, MFLAG_CAN_SPEAK) && !member_test_flag(member, MFLAG_NO_POSITIONAL)) {
count++;
}
}
if (count < 3) {
for (member = conference->members; member; member = member->next) {
if (member->channel && !member_test_flag(member, MFLAG_NO_POSITIONAL) && member->al) {
member->al->pos_x = 0;
member->al->pos_y = 0;
member->al->pos_z = 0;
member->al->setpos = 1;
if (stream) {
stream->write_function(stream, "Member %d (%s) 0.0:0.0:0.0\n", member->id, switch_channel_get_name(member->channel));
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Member %d (%s) 0.0:0.0:0.0\n",
member->id, switch_channel_get_name(member->channel));
}
}
}
goto end;
}
offset = 180 / (count - 1);
radius = 1.0f;
pos = -90.0f;
for (member = conference->members; member; member = member->next) {
if (!member->channel || member_test_flag(member, MFLAG_NO_POSITIONAL) || !member_test_flag(member, MFLAG_CAN_SPEAK)) {
continue;
}
if (!member->al) {
member->al = create_al(member->pool);
}
member_set_flag(member, MFLAG_POSITIONAL);
if (pos == 0) {
x = 0;
z = radius;
} else if (pos == -90) {
z = 0;
x = radius * -1;
} else if (pos == 90) {
z = 0;
x = radius;
} else if (pos < 0) {
z = cos((90+pos) * div) * radius;
x = sin((90+pos) * div) * radius * -1.0f;
} else {
x = cos(pos * div) * radius;
z = sin(pos * div) * radius;
}
member->al->pos_x = x;
member->al->pos_y = 0;
member->al->pos_z = z;
member->al->setpos = 1;
if (stream) {
stream->write_function(stream, "Member %d (%s) %0.2f:0.0:%0.2f\n", member->id, switch_channel_get_name(member->channel), x, z);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Member %d (%s) %0.2f:0.0:%0.2f\n",
member->id, switch_channel_get_name(member->channel), x, z);
}
pos += offset;
}
end:
switch_mutex_unlock(conference->member_mutex);
return;
}
void process_al(al_handle_t *al, void *data, switch_size_t datalen, int rate)
{
if (rate != 48000) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Only 48khz is supported.\n");
return;
}
if (!al->device) {
ALCint contextAttr[] = {
ALC_FORMAT_CHANNELS_SOFT, ALC_STEREO_SOFT,
ALC_FORMAT_TYPE_SOFT, ALC_SHORT_SOFT,
ALC_FREQUENCY, rate,
ALC_HRTF_SOFT, AL_TRUE,
0
};
switch_mutex_lock(mod_conference_globals.setup_mutex);
if ((al->device = alcLoopbackOpenDeviceSOFT(NULL))) {
const ALshort silence[16] = { 0 };
float orient[6] = { /*fwd:*/ 0., 0., -1., /*up:*/ 0., 1., 0. };
al->context = alcCreateContext(al->device, contextAttr);
alcSetThreadContext(al->context);
/* listener at origin, facing down -z (ears at 0.0m height) */
alListener3f( AL_POSITION, 0. ,0, 0. );
alListener3f( AL_VELOCITY, 0., 0., 0. );
alListenerfv( AL_ORIENTATION, orient );
alGenSources(1, &al->source);
alSourcef( al->source, AL_PITCH, 1.);
alSourcef( al->source, AL_GAIN, 1.);
alGenBuffers(2, al->buffer_in);
alBufferData(al->buffer_in[0], AL_FORMAT_MONO16, data, datalen, rate);
//alBufferData(al->buffer_in[0], AL_FORMAT_MONO16, NULL, 0, rate);
alBufferData(al->buffer_in[1], AL_FORMAT_MONO16, silence, sizeof(silence), rate);
alSourceQueueBuffers(al->source, 2, al->buffer_in);
alSourcePlay(al->source);
}
switch_mutex_unlock(mod_conference_globals.setup_mutex);
}
if (al->device) {
ALint processed = 0, state = 0;
//alcSetThreadContext(al->context);
alGetSourcei(al->source, AL_SOURCE_STATE, &state);
alGetSourcei(al->source, AL_BUFFERS_PROCESSED, &processed);
if (al->setpos) {
al->setpos = 0;
alSource3f(al->source, AL_POSITION, al->pos_x, al->pos_y, al->pos_z);
//alSource3f(al->source, AL_VELOCITY, .01, 0., 0.);
}
if (processed > 0) {
ALuint bufid;
alSourceUnqueueBuffers(al->source, 1, &bufid);
alBufferData(bufid, AL_FORMAT_MONO16, data, datalen, rate);
alSourceQueueBuffers(al->source, 1, &bufid);
}
if (state != AL_PLAYING) {
alSourcePlay(al->source);
}
alcRenderSamplesSOFT(al->device, data, datalen / 2);
}
}
#endif
void 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 ? "+++" : "---");
clear_avg(member);
}
}
#ifndef OPENAL_POSITIONING
switch_status_t parse_position(al_handle_t *al, const char *data)
{
return SWITCH_STATUS_FALSE;
}
#else
switch_status_t parse_position(al_handle_t *al, const char *data)
{
char *args[3];
int num;
char *dup;
dup = strdup((char *)data);
switch_assert(dup);
if ((num = switch_split(dup, ':', args)) != 3) {
return SWITCH_STATUS_FALSE;
}
al->pos_x = atof(args[0]);
al->pos_y = atof(args[1]);
al->pos_z = atof(args[2]);
al->setpos = 1;
switch_safe_free(dup);
return SWITCH_STATUS_SUCCESS;
}
#endif
#ifndef OPENAL_POSITIONING
switch_status_t member_parse_position(conference_member_t *member, const char *data)
{
return SWITCH_STATUS_FALSE;
}
#else
switch_status_t member_parse_position(conference_member_t *member, const char *data)
{
switch_status_t status = SWITCH_STATUS_FALSE;
if (member->al) {
status = parse_position(member->al, data);
}
return status;
}
#endif
#ifdef OPENAL_POSITIONING
void close_al(al_handle_t *al)
{
if (!al) return;
switch_mutex_lock(mod_conference_globals.setup_mutex);
if (al->source) {
alDeleteSources(1, &al->source);
al->source = 0;
}
if (al->buffer_in[0]) {
alDeleteBuffers(2, al->buffer_in);
al->buffer_in[0] = 0;
al->buffer_in[1] = 0;
}
if (al->context) {
alcDestroyContext(al->context);
al->context = 0;
}
if (al->device) {
alcCloseDevice(al->device);
al->device = NULL;
}
switch_mutex_unlock(mod_conference_globals.setup_mutex);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,761 @@
/*
* 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/
*
* 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):
*
* 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>
inline switch_bool_t cdr_test_mflag(conference_cdr_node_t *np, member_flag_t mflag)
{
return !!np->mflags[mflag];
}
const char *audio_flow(conference_member_t *member)
{
const char *flow = "sendrecv";
if (!member_test_flag(member, MFLAG_CAN_SPEAK)) {
flow = "recvonly";
}
if (member->channel && switch_channel_test_flag(member->channel, CF_HOLD)) {
flow = member_test_flag(member, MFLAG_CAN_SPEAK) ? "sendonly" : "inactive";
}
return flow;
}
char *conference_rfc4579_render(conference_obj_t *conference, switch_event_t *event, switch_event_t *revent)
{
switch_xml_t xml, x_tag, x_tag1, x_tag2, x_tag3, x_tag4;
char tmp[30];
const char *domain; const char *name;
char *dup_domain = NULL;
char *uri;
int off = 0, off1 = 0, off2 = 0, off3 = 0, off4 = 0;
conference_cdr_node_t *np;
char *tmpp = tmp;
char *xml_text = NULL;
if (!(xml = switch_xml_new("conference-info"))) {
abort();
}
switch_mutex_lock(conference->mutex);
switch_snprintf(tmp, sizeof(tmp), "%u", conference->doc_version);
conference->doc_version++;
switch_mutex_unlock(conference->mutex);
if (!event || !(name = switch_event_get_header(event, "conference-name"))) {
if (!(name = conference->name)) {
name = "conference";
}
}
if (!event || !(domain = switch_event_get_header(event, "conference-domain"))) {
if (!(domain = conference->domain)) {
dup_domain = switch_core_get_domain(SWITCH_TRUE);
if (!(domain = dup_domain)) {
domain = "cluecon.com";
}
}
}
switch_xml_set_attr_d(xml, "version", tmpp);
switch_xml_set_attr_d(xml, "state", "full");
switch_xml_set_attr_d(xml, "xmlns", "urn:ietf:params:xml:ns:conference-info");
uri = switch_mprintf("sip:%s@%s", name, domain);
switch_xml_set_attr_d(xml, "entity", uri);
if (!(x_tag = switch_xml_add_child_d(xml, "conference-description", off++))) {
abort();
}
if (!(x_tag1 = switch_xml_add_child_d(x_tag, "display-text", off1++))) {
abort();
}
switch_xml_set_txt_d(x_tag1, conference->desc ? conference->desc : "FreeSWITCH Conference");
if (!(x_tag1 = switch_xml_add_child_d(x_tag, "conf-uris", off1++))) {
abort();
}
if (!(x_tag2 = switch_xml_add_child_d(x_tag1, "entry", off2++))) {
abort();
}
if (!(x_tag3 = switch_xml_add_child_d(x_tag2, "uri", off3++))) {
abort();
}
switch_xml_set_txt_d(x_tag3, uri);
if (!(x_tag = switch_xml_add_child_d(xml, "conference-state", off++))) {
abort();
}
if (!(x_tag1 = switch_xml_add_child_d(x_tag, "user-count", off1++))) {
abort();
}
switch_snprintf(tmp, sizeof(tmp), "%u", conference->count);
switch_xml_set_txt_d(x_tag1, tmpp);
#if 0
if (conference->count == 0) {
switch_event_add_header(revent, SWITCH_STACK_BOTTOM, "notfound", "true");
}
#endif
if (!(x_tag1 = switch_xml_add_child_d(x_tag, "active", off1++))) {
abort();
}
switch_xml_set_txt_d(x_tag1, "true");
off1 = off2 = off3 = off4 = 0;
if (!(x_tag = switch_xml_add_child_d(xml, "users", off++))) {
abort();
}
switch_mutex_lock(conference->member_mutex);
for (np = conference->cdr_nodes; np; np = np->next) {
char *user_uri = NULL;
switch_channel_t *channel = NULL;
if (!np->cp || (np->member && !np->member->session) || np->leave_time) { /* for now we'll remove participants when the leave */
continue;
}
if (np->member && np->member->session) {
channel = switch_core_session_get_channel(np->member->session);
}
if (!(x_tag1 = switch_xml_add_child_d(x_tag, "user", off1++))) {
abort();
}
if (channel) {
const char *uri = switch_channel_get_variable_dup(channel, "conference_invite_uri", SWITCH_FALSE, -1);
if (uri) {
user_uri = strdup(uri);
}
}
if (!user_uri) {
user_uri = switch_mprintf("sip:%s@%s", np->cp->caller_id_number, domain);
}
switch_xml_set_attr_d(x_tag1, "state", "full");
switch_xml_set_attr_d(x_tag1, "entity", user_uri);
if (!(x_tag2 = switch_xml_add_child_d(x_tag1, "display-text", off2++))) {
abort();
}
switch_xml_set_txt_d(x_tag2, np->cp->caller_id_name);
if (!(x_tag2 = switch_xml_add_child_d(x_tag1, "endpoint", off2++))) {
abort();
}
switch_xml_set_attr_d(x_tag2, "entity", user_uri);
if (!(x_tag3 = switch_xml_add_child_d(x_tag2, "display-text", off3++))) {
abort();
}
switch_xml_set_txt_d(x_tag3, np->cp->caller_id_name);
if (!(x_tag3 = switch_xml_add_child_d(x_tag2, "status", off3++))) {
abort();
}
switch_xml_set_txt_d(x_tag3, np->leave_time ? "disconnected" : "connected");
if (!(x_tag3 = switch_xml_add_child_d(x_tag2, "joining-info", off3++))) {
abort();
}
if (!(x_tag4 = switch_xml_add_child_d(x_tag3, "when", off4++))) {
abort();
} else {
switch_time_exp_t tm;
switch_size_t retsize;
const char *fmt = "%Y-%m-%dT%H:%M:%S%z";
char *p;
switch_time_exp_lt(&tm, (switch_time_t) conference->start_time * 1000000);
switch_strftime_nocheck(tmp, &retsize, sizeof(tmp), fmt, &tm);
p = end_of_p(tmpp) -1;
snprintf(p, 4, ":00");
switch_xml_set_txt_d(x_tag4, tmpp);
}
/** ok so this is in the rfc but not the xsd
if (!(x_tag3 = switch_xml_add_child_d(x_tag2, "joining-method", off3++))) {
abort();
}
switch_xml_set_txt_d(x_tag3, np->cp->direction == SWITCH_CALL_DIRECTION_INBOUND ? "dialed-in" : "dialed-out");
*/
if (np->member) {
const char *var;
//char buf[1024];
//switch_snprintf(buf, sizeof(buf), "conf_%s_%s_%s", conference->name, conference->domain, np->cp->caller_id_number);
//switch_channel_set_variable(channel, "conference_call_key", buf);
if (!(x_tag3 = switch_xml_add_child_d(x_tag2, "media", off3++))) {
abort();
}
snprintf(tmp, sizeof(tmp), "%ua", np->member->id);
switch_xml_set_attr_d(x_tag3, "id", tmpp);
if (!(x_tag4 = switch_xml_add_child_d(x_tag3, "type", off4++))) {
abort();
}
switch_xml_set_txt_d(x_tag4, "audio");
if ((var = switch_channel_get_variable(channel, "rtp_use_ssrc"))) {
if (!(x_tag4 = switch_xml_add_child_d(x_tag3, "src-id", off4++))) {
abort();
}
switch_xml_set_txt_d(x_tag4, var);
}
if (!(x_tag4 = switch_xml_add_child_d(x_tag3, "status", off4++))) {
abort();
}
switch_xml_set_txt_d(x_tag4, audio_flow(np->member));
if (switch_channel_test_flag(channel, CF_VIDEO)) {
off4 = 0;
if (!(x_tag3 = switch_xml_add_child_d(x_tag2, "media", off3++))) {
abort();
}
snprintf(tmp, sizeof(tmp), "%uv", np->member->id);
switch_xml_set_attr_d(x_tag3, "id", tmpp);
if (!(x_tag4 = switch_xml_add_child_d(x_tag3, "type", off4++))) {
abort();
}
switch_xml_set_txt_d(x_tag4, "video");
if ((var = switch_channel_get_variable(channel, "rtp_use_video_ssrc"))) {
if (!(x_tag4 = switch_xml_add_child_d(x_tag3, "src-id", off4++))) {
abort();
}
switch_xml_set_txt_d(x_tag4, var);
}
if (!(x_tag4 = switch_xml_add_child_d(x_tag3, "status", off4++))) {
abort();
}
switch_xml_set_txt_d(x_tag4, switch_channel_test_flag(channel, CF_HOLD) ? "sendonly" : "sendrecv");
}
}
switch_safe_free(user_uri);
}
switch_mutex_unlock(conference->member_mutex);
off1 = off2 = off3 = off4 = 0;
xml_text = switch_xml_toxml(xml, SWITCH_TRUE);
switch_xml_free(xml);
switch_safe_free(dup_domain);
switch_safe_free(uri);
return xml_text;
}
cJSON *conference_json_render(conference_obj_t *conference, cJSON *req)
{
char tmp[30];
const char *domain; const char *name;
char *dup_domain = NULL;
char *uri;
conference_cdr_node_t *np;
char *tmpp = tmp;
cJSON *json = cJSON_CreateObject(), *jusers = NULL, *jold_users = NULL, *juser = NULL, *jvars = NULL;
switch_assert(json);
switch_mutex_lock(conference->mutex);
switch_snprintf(tmp, sizeof(tmp), "%u", conference->doc_version);
conference->doc_version++;
switch_mutex_unlock(conference->mutex);
if (!(name = conference->name)) {
name = "conference";
}
if (!(domain = conference->domain)) {
dup_domain = switch_core_get_domain(SWITCH_TRUE);
if (!(domain = dup_domain)) {
domain = "cluecon.com";
}
}
uri = switch_mprintf("%s@%s", name, domain);
json_add_child_string(json, "entity", uri);
json_add_child_string(json, "conferenceDescription", conference->desc ? conference->desc : "FreeSWITCH Conference");
json_add_child_string(json, "conferenceState", "active");
switch_snprintf(tmp, sizeof(tmp), "%u", conference->count);
json_add_child_string(json, "userCount", tmp);
jusers = json_add_child_array(json, "users");
jold_users = json_add_child_array(json, "oldUsers");
switch_mutex_lock(conference->member_mutex);
for (np = conference->cdr_nodes; np; np = np->next) {
char *user_uri = NULL;
switch_channel_t *channel = NULL;
switch_time_exp_t tm;
switch_size_t retsize;
const char *fmt = "%Y-%m-%dT%H:%M:%S%z";
char *p;
if (np->record_path || !np->cp) {
continue;
}
//if (!np->cp || (np->member && !np->member->session) || np->leave_time) { /* for now we'll remove participants when they leave */
//continue;
//}
if (np->member && np->member->session) {
channel = switch_core_session_get_channel(np->member->session);
}
juser = cJSON_CreateObject();
if (channel) {
const char *uri = switch_channel_get_variable_dup(channel, "conference_invite_uri", SWITCH_FALSE, -1);
if (uri) {
user_uri = strdup(uri);
}
}
if (np->cp) {
if (!user_uri) {
user_uri = switch_mprintf("%s@%s", np->cp->caller_id_number, domain);
}
json_add_child_string(juser, "entity", user_uri);
json_add_child_string(juser, "displayText", np->cp->caller_id_name);
}
//if (np->record_path) {
//json_add_child_string(juser, "recordingPATH", np->record_path);
//}
json_add_child_string(juser, "status", np->leave_time ? "disconnected" : "connected");
switch_time_exp_lt(&tm, (switch_time_t) conference->start_time * 1000000);
switch_strftime_nocheck(tmp, &retsize, sizeof(tmp), fmt, &tm);
p = end_of_p(tmpp) -1;
snprintf(p, 4, ":00");
json_add_child_string(juser, "joinTime", tmpp);
snprintf(tmp, sizeof(tmp), "%u", np->id);
json_add_child_string(juser, "memberId", tmp);
jvars = cJSON_CreateObject();
if (!np->member && np->var_event) {
switch_json_add_presence_data_cols(np->var_event, jvars, "PD-");
} else if (np->member) {
const char *var;
const char *prefix = NULL;
switch_event_t *var_event = NULL;
switch_event_header_t *hp;
int all = 0;
switch_channel_get_variables(channel, &var_event);
if ((prefix = switch_event_get_header(var_event, "json_conf_var_prefix"))) {
all = strcasecmp(prefix, "__all__");
} else {
prefix = "json_";
}
for(hp = var_event->headers; hp; hp = hp->next) {
if (all || !strncasecmp(hp->name, prefix, strlen(prefix))) {
json_add_child_string(jvars, hp->name, hp->value);
}
}
switch_json_add_presence_data_cols(var_event, jvars, "PD-");
switch_event_destroy(&var_event);
if ((var = switch_channel_get_variable(channel, "rtp_use_ssrc"))) {
json_add_child_string(juser, "rtpAudioSSRC", var);
}
json_add_child_string(juser, "rtpAudioDirection", audio_flow(np->member));
if (switch_channel_test_flag(channel, CF_VIDEO)) {
if ((var = switch_channel_get_variable(channel, "rtp_use_video_ssrc"))) {
json_add_child_string(juser, "rtpVideoSSRC", var);
}
json_add_child_string(juser, "rtpVideoDirection", switch_channel_test_flag(channel, CF_HOLD) ? "sendonly" : "sendrecv");
}
}
if (jvars) {
json_add_child_obj(juser, "variables", jvars);
}
cJSON_AddItemToArray(np->leave_time ? jold_users : jusers, juser);
switch_safe_free(user_uri);
}
switch_mutex_unlock(conference->member_mutex);
switch_safe_free(dup_domain);
switch_safe_free(uri);
return json;
}
void conference_cdr_del(conference_member_t *member)
{
if (member->channel) {
switch_channel_get_variables(member->channel, &member->cdr_node->var_event);
}
if (member->cdr_node) {
member->cdr_node->leave_time = switch_epoch_time_now(NULL);
memcpy(member->cdr_node->mflags, member->flags, sizeof(member->flags));
member->cdr_node->member = NULL;
}
}
void conference_cdr_add(conference_member_t *member)
{
conference_cdr_node_t *np;
switch_caller_profile_t *cp;
switch_channel_t *channel;
np = switch_core_alloc(member->conference->pool, sizeof(*np));
np->next = member->conference->cdr_nodes;
member->conference->cdr_nodes = member->cdr_node = np;
member->cdr_node->join_time = switch_epoch_time_now(NULL);
member->cdr_node->member = member;
if (!member->session) {
member->cdr_node->record_path = switch_core_strdup(member->conference->pool, member->rec_path);
return;
}
channel = switch_core_session_get_channel(member->session);
if (!(cp = switch_channel_get_caller_profile(channel))) {
return;
}
member->cdr_node->cp = switch_caller_profile_dup(member->conference->pool, cp);
member->cdr_node->id = member->id;
}
void conference_cdr_rejected(conference_obj_t *conference, switch_channel_t *channel, cdr_reject_reason_t reason)
{
conference_cdr_reject_t *rp;
switch_caller_profile_t *cp;
rp = switch_core_alloc(conference->pool, sizeof(*rp));
rp->next = conference->cdr_rejected;
conference->cdr_rejected = rp;
rp->reason = reason;
rp->reject_time = switch_epoch_time_now(NULL);
if (!(cp = switch_channel_get_caller_profile(channel))) {
return;
}
rp->cp = switch_caller_profile_dup(conference->pool, cp);
}
void conference_cdr_render(conference_obj_t *conference)
{
switch_xml_t cdr, x_ptr, x_member, x_members, x_conference, x_cp, x_flags, x_tag, x_rejected, x_attempt;
conference_cdr_node_t *np;
conference_cdr_reject_t *rp;
int cdr_off = 0, conf_off = 0;
char str[512];
char *path = NULL, *xml_text;
int fd;
if (zstr(conference->log_dir) && (conference->cdr_event_mode == CDRE_NONE)) return;
if (!conference->cdr_nodes && !conference->cdr_rejected) return;
if (!(cdr = switch_xml_new("cdr"))) {
abort();
}
if (!(x_conference = switch_xml_add_child_d(cdr, "conference", cdr_off++))) {
abort();
}
if (!(x_ptr = switch_xml_add_child_d(x_conference, "name", conf_off++))) {
abort();
}
switch_xml_set_txt_d(x_ptr, conference->name);
if (!(x_ptr = switch_xml_add_child_d(x_conference, "hostname", conf_off++))) {
abort();
}
switch_xml_set_txt_d(x_ptr, switch_core_get_hostname());
if (!(x_ptr = switch_xml_add_child_d(x_conference, "rate", conf_off++))) {
abort();
}
switch_snprintf(str, sizeof(str), "%d", conference->rate);
switch_xml_set_txt_d(x_ptr, str);
if (!(x_ptr = switch_xml_add_child_d(x_conference, "interval", conf_off++))) {
abort();
}
switch_snprintf(str, sizeof(str), "%d", conference->interval);
switch_xml_set_txt_d(x_ptr, str);
if (!(x_ptr = switch_xml_add_child_d(x_conference, "start_time", conf_off++))) {
abort();
}
switch_xml_set_attr_d(x_ptr, "type", "UNIX-epoch");
switch_snprintf(str, sizeof(str), "%ld", (long)conference->start_time);
switch_xml_set_txt_d(x_ptr, str);
if (!(x_ptr = switch_xml_add_child_d(x_conference, "end_time", conf_off++))) {
abort();
}
switch_xml_set_attr_d(x_ptr, "endconf_forced", conference_test_flag(conference, CFLAG_ENDCONF_FORCED) ? "true" : "false");
switch_xml_set_attr_d(x_ptr, "type", "UNIX-epoch");
switch_snprintf(str, sizeof(str), "%ld", (long)conference->end_time);
switch_xml_set_txt_d(x_ptr, str);
if (!(x_members = switch_xml_add_child_d(x_conference, "members", conf_off++))) {
abort();
}
for (np = conference->cdr_nodes; np; np = np->next) {
int member_off = 0;
int flag_off = 0;
if (!(x_member = switch_xml_add_child_d(x_members, "member", conf_off++))) {
abort();
}
switch_xml_set_attr_d(x_member, "type", np->cp ? "caller" : "recording_node");
if (!(x_ptr = switch_xml_add_child_d(x_member, "join_time", member_off++))) {
abort();
}
switch_xml_set_attr_d(x_ptr, "type", "UNIX-epoch");
switch_snprintf(str, sizeof(str), "%ld", (long) np->join_time);
switch_xml_set_txt_d(x_ptr, str);
if (!(x_ptr = switch_xml_add_child_d(x_member, "leave_time", member_off++))) {
abort();
}
switch_xml_set_attr_d(x_ptr, "type", "UNIX-epoch");
switch_snprintf(str, sizeof(str), "%ld", (long) np->leave_time);
switch_xml_set_txt_d(x_ptr, str);
if (np->cp) {
x_flags = switch_xml_add_child_d(x_member, "flags", member_off++);
switch_assert(x_flags);
x_tag = switch_xml_add_child_d(x_flags, "is_moderator", flag_off++);
switch_xml_set_txt_d(x_tag, cdr_test_mflag(np, MFLAG_MOD) ? "true" : "false");
x_tag = switch_xml_add_child_d(x_flags, "end_conference", flag_off++);
switch_xml_set_txt_d(x_tag, cdr_test_mflag(np, MFLAG_ENDCONF) ? "true" : "false");
x_tag = switch_xml_add_child_d(x_flags, "was_kicked", flag_off++);
switch_xml_set_txt_d(x_tag, cdr_test_mflag(np, MFLAG_KICKED) ? "true" : "false");
x_tag = switch_xml_add_child_d(x_flags, "is_ghost", flag_off++);
switch_xml_set_txt_d(x_tag, cdr_test_mflag(np, MFLAG_GHOST) ? "true" : "false");
if (!(x_cp = switch_xml_add_child_d(x_member, "caller_profile", member_off++))) {
abort();
}
switch_ivr_set_xml_profile_data(x_cp, np->cp, 0);
}
if (!zstr(np->record_path)) {
if (!(x_ptr = switch_xml_add_child_d(x_member, "record_path", member_off++))) {
abort();
}
switch_xml_set_txt_d(x_ptr, np->record_path);
}
}
if (!(x_rejected = switch_xml_add_child_d(x_conference, "rejected", conf_off++))) {
abort();
}
for (rp = conference->cdr_rejected; rp; rp = rp->next) {
int attempt_off = 0;
int tag_off = 0;
if (!(x_attempt = switch_xml_add_child_d(x_rejected, "attempt", attempt_off++))) {
abort();
}
if (!(x_ptr = switch_xml_add_child_d(x_attempt, "reason", tag_off++))) {
abort();
}
if (rp->reason == CDRR_LOCKED) {
switch_xml_set_txt_d(x_ptr, "conference_locked");
} else if (rp->reason == CDRR_MAXMEMBERS) {
switch_xml_set_txt_d(x_ptr, "max_members_reached");
} else if (rp->reason == CDRR_PIN) {
switch_xml_set_txt_d(x_ptr, "invalid_pin");
}
if (!(x_ptr = switch_xml_add_child_d(x_attempt, "reject_time", tag_off++))) {
abort();
}
switch_xml_set_attr_d(x_ptr, "type", "UNIX-epoch");
switch_snprintf(str, sizeof(str), "%ld", (long) rp->reject_time);
switch_xml_set_txt_d(x_ptr, str);
if (rp->cp) {
if (!(x_cp = switch_xml_add_child_d(x_attempt, "caller_profile", attempt_off++))) {
abort();
}
switch_ivr_set_xml_profile_data(x_cp, rp->cp, 0);
}
}
xml_text = switch_xml_toxml(cdr, SWITCH_TRUE);
if (!zstr(conference->log_dir)) {
path = switch_mprintf("%s%s%s.cdr.xml", conference->log_dir, SWITCH_PATH_SEPARATOR, conference->uuid_str);
#ifdef _MSC_VER
if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) > -1) {
#else
if ((fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) > -1) {
#endif
int wrote;
wrote = write(fd, xml_text, (unsigned) strlen(xml_text));
wrote++;
close(fd);
fd = -1;
} else {
char ebuf[512] = { 0 };
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error writing [%s][%s]\n",
path, switch_strerror_r(errno, ebuf, sizeof(ebuf)));
}
if (conference->cdr_event_mode != CDRE_NONE) {
switch_event_t *event;
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_CDR) == SWITCH_STATUS_SUCCESS)
// if (switch_event_create(&event, SWITCH_EVENT_CDR) == SWITCH_STATUS_SUCCESS)
{
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CDR-Source", CONF_EVENT_CDR);
if (conference->cdr_event_mode == CDRE_AS_CONTENT) {
switch_event_set_body(event, xml_text);
} else {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "CDR-Path", path);
}
switch_event_fire(&event);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not create CDR event");
}
}
}
switch_safe_free(path);
switch_safe_free(xml_text);
switch_xml_free(cdr);
}

View File

@ -0,0 +1,828 @@
/*
* 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/
*
* 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):
*
* 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>
void conference_mod_event_channel_handler(const char *event_channel, cJSON *json, const char *key, switch_event_channel_id_t id)
{
cJSON *data, *addobj = NULL;
const char *action = NULL;
char *value = NULL;
cJSON *jid = 0;
char *conf_name = strdup(event_channel + 15);
int cid = 0;
char *p;
switch_stream_handle_t stream = { 0 };
char *exec = NULL;
cJSON *msg, *jdata, *jvalue;
char *argv[10] = {0};
int argc = 0;
if (conf_name && (p = strchr(conf_name, '@'))) {
*p = '\0';
}
if ((data = cJSON_GetObjectItem(json, "data"))) {
action = cJSON_GetObjectCstr(data, "command");
if ((jid = cJSON_GetObjectItem(data, "id"))) {
cid = jid->valueint;
}
if ((jvalue = cJSON_GetObjectItem(data, "value"))) {
if (jvalue->type == cJSON_Array) {
int i;
argc = cJSON_GetArraySize(jvalue);
if (argc > 10) argc = 10;
for (i = 0; i < argc; i++) {
cJSON *str = cJSON_GetArrayItem(jvalue, i);
if (str->type == cJSON_String) {
argv[i] = str->valuestring;
}
}
} else if (jvalue->type == cJSON_String) {
value = jvalue->valuestring;
argv[argc++] = value;
}
}
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "conf %s CMD %s [%s] %d\n", conf_name, key, action, cid);
if (zstr(action)) {
goto end;
}
SWITCH_STANDARD_STREAM(stream);
if (!strcasecmp(action, "kick") ||
!strcasecmp(action, "mute") ||
!strcasecmp(action, "unmute") ||
!strcasecmp(action, "tmute") ||
!strcasecmp(action, "vmute") ||
!strcasecmp(action, "unvmute") ||
!strcasecmp(action, "tvmute")
) {
exec = switch_mprintf("%s %s %d", conf_name, action, cid);
} else if (!strcasecmp(action, "volume_in") ||
!strcasecmp(action, "volume_out") ||
!strcasecmp(action, "vid-res-id") ||
!strcasecmp(action, "vid-floor") ||
!strcasecmp(action, "vid-layer") ||
!strcasecmp(action, "vid-canvas") ||
!strcasecmp(action, "vid-watching-canvas") ||
!strcasecmp(action, "vid-banner")) {
exec = switch_mprintf("%s %s %d %s", conf_name, action, cid, argv[0]);
} else if (!strcasecmp(action, "play") || !strcasecmp(action, "stop")) {
exec = switch_mprintf("%s %s %s", conf_name, action, argv[0]);
} else if (!strcasecmp(action, "recording") || !strcasecmp(action, "vid-layout") || !strcasecmp(action, "vid-write-png")) {
if (!argv[1]) {
argv[1] = "all";
}
exec = switch_mprintf("%s %s %s %s", conf_name, action, argv[0], argv[1]);
} else if (!strcasecmp(action, "transfer") && cid) {
conference_member_t *member;
conference_obj_t *conference;
exec = switch_mprintf("%s %s %s", argv[0], switch_str_nil(argv[1]), switch_str_nil(argv[2]));
stream.write_function(&stream, "+OK Call transferred to %s", argv[0]);
if ((conference = conference_find(conf_name, NULL))) {
if ((member = conference_member_get(conference, cid))) {
switch_ivr_session_transfer(member->session, argv[0], argv[1], argv[2]);
switch_thread_rwlock_unlock(member->rwlock);
}
switch_thread_rwlock_unlock(conference->rwlock);
}
goto end;
} else if (!strcasecmp(action, "list-videoLayouts")) {
switch_hash_index_t *hi;
void *val;
const void *vvar;
cJSON *array = cJSON_CreateArray();
conference_obj_t *conference = NULL;
if ((conference = conference_find(conf_name, NULL))) {
switch_mutex_lock(mod_conference_globals.setup_mutex);
if (conference->layout_hash) {
for (hi = switch_core_hash_first(conference->layout_hash); hi; hi = switch_core_hash_next(&hi)) {
switch_core_hash_this(hi, &vvar, NULL, &val);
cJSON_AddItemToArray(array, cJSON_CreateString((char *)vvar));
}
}
if (conference->layout_group_hash) {
for (hi = switch_core_hash_first(conference->layout_group_hash); hi; hi = switch_core_hash_next(&hi)) {
char *name;
switch_core_hash_this(hi, &vvar, NULL, &val);
name = switch_mprintf("group:%s", (char *)vvar);
cJSON_AddItemToArray(array, cJSON_CreateString(name));
free(name);
}
}
switch_mutex_unlock(mod_conference_globals.setup_mutex);
switch_thread_rwlock_unlock(conference->rwlock);
}
addobj = array;
}
if (exec) {
conf_api_main_real(exec, NULL, &stream);
}
end:
msg = cJSON_CreateObject();
jdata = json_add_child_obj(msg, "data", NULL);
cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(event_channel));
cJSON_AddItemToObject(jdata, "action", cJSON_CreateString("response"));
if (addobj) {
cJSON_AddItemToObject(jdata, "conf-command", cJSON_CreateString(action));
cJSON_AddItemToObject(jdata, "response", cJSON_CreateString("OK"));
cJSON_AddItemToObject(jdata, "responseData", addobj);
} else if (exec) {
cJSON_AddItemToObject(jdata, "conf-command", cJSON_CreateString(exec));
cJSON_AddItemToObject(jdata, "response", cJSON_CreateString((char *)stream.data));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ALERT,"RES [%s][%s]\n", exec, (char *)stream.data);
} else {
cJSON_AddItemToObject(jdata, "error", cJSON_CreateString("Invalid Command"));
}
switch_event_channel_broadcast(event_channel, &msg, __FILE__, mod_conference_globals.event_channel_id);
switch_safe_free(stream.data);
switch_safe_free(exec);
switch_safe_free(conf_name);
}
void conference_la_event_channel_handler(const char *event_channel, cJSON *json, const char *key, switch_event_channel_id_t id)
{
switch_live_array_parse_json(json, mod_conference_globals.event_channel_id);
}
void conference_event_channel_handler(const char *event_channel, cJSON *json, const char *key, switch_event_channel_id_t id)
{
char *domain = NULL, *name = NULL;
conference_obj_t *conference = NULL;
cJSON *data, *reply = NULL, *conf_desc = NULL;
const char *action = NULL;
char *dup = NULL;
if ((data = cJSON_GetObjectItem(json, "data"))) {
action = cJSON_GetObjectCstr(data, "action");
}
if (!action) action = "";
reply = cJSON_Duplicate(json, 1);
cJSON_DeleteItemFromObject(reply, "data");
if ((name = strchr(event_channel, '.'))) {
dup = strdup(name + 1);
switch_assert(dup);
name = dup;
if ((domain = strchr(name, '@'))) {
*domain++ = '\0';
}
}
if (!strcasecmp(action, "bootstrap")) {
if (!zstr(name) && (conference = conference_find(name, domain))) {
conf_desc = conference_json_render(conference, json);
} else {
conf_desc = cJSON_CreateObject();
json_add_child_string(conf_desc, "conferenceDescription", "FreeSWITCH Conference");
json_add_child_string(conf_desc, "conferenceState", "inactive");
json_add_child_array(conf_desc, "users");
json_add_child_array(conf_desc, "oldUsers");
}
} else {
conf_desc = cJSON_CreateObject();
json_add_child_string(conf_desc, "error", "Invalid action");
}
json_add_child_string(conf_desc, "action", "conferenceDescription");
cJSON_AddItemToObject(reply, "data", conf_desc);
switch_safe_free(dup);
switch_event_channel_broadcast(event_channel, &reply, "mod_conference", mod_conference_globals.event_channel_id);
}
void send_json_event(conference_obj_t *conference)
{
cJSON *event, *conf_desc = NULL;
char *name = NULL, *domain = NULL, *dup_domain = NULL;
char *event_channel = NULL;
if (!conference_test_flag(conference, CFLAG_JSON_EVENTS)) {
return;
}
conf_desc = conference_json_render(conference, NULL);
if (!(name = conference->name)) {
name = "conference";
}
if (!(domain = conference->domain)) {
dup_domain = switch_core_get_domain(SWITCH_TRUE);
if (!(domain = dup_domain)) {
domain = "cluecon.com";
}
}
event_channel = switch_mprintf("conference.%q@%q", name, domain);
event = cJSON_CreateObject();
json_add_child_string(event, "eventChannel", event_channel);
cJSON_AddItemToObject(event, "data", conf_desc);
switch_event_channel_broadcast(event_channel, &event, "mod_conference", mod_conference_globals.event_channel_id);
switch_safe_free(dup_domain);
switch_safe_free(event_channel);
}
void conference_command_handler(switch_live_array_t *la, const char *cmd, const char *sessid, cJSON *jla, void *user_data)
{
}
void 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) || member_test_flag(member, MFLAG_SECOND_SCREEN)) {
return;
}
switch_live_array_lock(member->conference->la);
if (conference_test_flag(member->conference, CFLAG_JSON_STATUS)) {
json = cJSON_CreateObject();
audio = cJSON_CreateObject();
cJSON_AddItemToObject(audio, "muted", cJSON_CreateBool(!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(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(!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_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 (!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 (member_test_flag(member, MFLAG_TALKING)) {
str = "TALKING (FLOOR)";
} else {
str = "FLOOR";
}
} else if (member_test_flag(member, MFLAG_TALKING)) {
str = "TALKING";
} else {
str = "ACTIVE";
}
if (switch_channel_test_flag(member->channel, CF_VIDEO)) {
if (!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);
}
void adv_la(conference_obj_t *conference, conference_member_t *member, switch_bool_t join)
{
//if (member->video_flow == SWITCH_MEDIA_FLOW_SENDONLY) {
switch_channel_set_flag(member->channel, CF_VIDEO_REFRESH_REQ);
switch_core_media_gen_key_frame(member->session);
//}
if (conference && conference->la && member->session &&
!switch_channel_test_flag(member->channel, CF_VIDEO_ONLY)) {
cJSON *msg, *data;
const char *uuid = switch_core_session_get_uuid(member->session);
const char *cookie = switch_channel_get_variable(member->channel, "event_channel_cookie");
const char *event_channel = cookie ? cookie : uuid;
switch_event_t *variables;
switch_event_header_t *hp;
msg = cJSON_CreateObject();
data = json_add_child_obj(msg, "pvtData", NULL);
cJSON_AddItemToObject(msg, "eventChannel", cJSON_CreateString(event_channel));
cJSON_AddItemToObject(msg, "eventType", cJSON_CreateString("channelPvtData"));
cJSON_AddItemToObject(data, "action", cJSON_CreateString(join ? "conference-liveArray-join" : "conference-liveArray-part"));
cJSON_AddItemToObject(data, "laChannel", cJSON_CreateString(conference->la_event_channel));
cJSON_AddItemToObject(data, "laName", cJSON_CreateString(conference->la_name));
cJSON_AddItemToObject(data, "role", cJSON_CreateString(member_test_flag(member, MFLAG_MOD) ? "moderator" : "participant"));
cJSON_AddItemToObject(data, "chatID", cJSON_CreateString(conference->chat_id));
cJSON_AddItemToObject(data, "canvasCount", cJSON_CreateNumber(conference->canvas_count));
if (member_test_flag(member, MFLAG_SECOND_SCREEN)) {
cJSON_AddItemToObject(data, "secondScreen", cJSON_CreateTrue());
}
if (member_test_flag(member, MFLAG_MOD)) {
cJSON_AddItemToObject(data, "modChannel", cJSON_CreateString(conference->mod_event_channel));
}
switch_core_get_variables(&variables);
for (hp = variables->headers; hp; hp = hp->next) {
if (!strncasecmp(hp->name, "conf_verto_", 11)) {
char *var = hp->name + 11;
if (var) {
cJSON_AddItemToObject(data, var, cJSON_CreateString(hp->value));
}
}
}
switch_event_destroy(&variables);
switch_event_channel_broadcast(event_channel, &msg, "mod_conference", mod_conference_globals.event_channel_id);
if (cookie) {
switch_event_channel_permission_modify(cookie, conference->la_event_channel, join);
switch_event_channel_permission_modify(cookie, conference->mod_event_channel, join);
}
}
}
void send_rfc_event(conference_obj_t *conference)
{
switch_event_t *event;
char *body;
char *name = NULL, *domain = NULL, *dup_domain = NULL;
if (!conference_test_flag(conference, CFLAG_RFC4579)) {
return;
}
if (!(name = conference->name)) {
name = "conference";
}
if (!(domain = conference->domain)) {
dup_domain = switch_core_get_domain(SWITCH_TRUE);
if (!(domain = dup_domain)) {
domain = "cluecon.com";
}
}
if (switch_event_create(&event, SWITCH_EVENT_CONFERENCE_DATA) == SWITCH_STATUS_SUCCESS) {
event->flags |= EF_UNIQ_HEADERS;
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "conference-name", name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "conference-domain", domain);
body = conference_rfc4579_render(conference, NULL, event);
switch_event_add_body(event, "%s", body);
free(body);
switch_event_fire(&event);
}
switch_safe_free(dup_domain);
}
switch_status_t conference_add_event_data(conference_obj_t *conference, switch_event_t *event)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Size", "%u", conference->count);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Ghosts", "%u", conference->count_ghosts);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Conference-Profile-Name", conference->profile_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Conference-Unique-ID", conference->uuid_str);
return status;
}
switch_status_t conference_add_event_member_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_add_event_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", member_test_flag(member, MFLAG_CAN_HEAR) ? "true" : "false" );
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "See", "%s", member_test_flag(member, MFLAG_CAN_BE_SEEN) ? "true" : "false" );
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Speak", "%s", member_test_flag(member, MFLAG_CAN_SPEAK) ? "true" : "false" );
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Talking", "%s", member_test_flag(member, MFLAG_TALKING) ? "true" : "false" );
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Mute-Detect", "%s", 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", member_test_flag(member, MFLAG_MOD) ? "moderator" : "member");
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-Ghost", "%s", 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;
}
/* send a message to every member of the conference */
void chat_message_broadcast(conference_obj_t *conference, switch_event_t *event)
{
conference_member_t *member = NULL;
switch_event_t *processed;
switch_assert(conference != NULL);
switch_event_create(&processed, SWITCH_EVENT_CHANNEL_DATA);
switch_mutex_lock(conference->member_mutex);
for (member = conference->members; member; member = member->next) {
if (member->session && !member_test_flag(member, MFLAG_NOCHANNEL)) {
const char *presence_id = switch_channel_get_variable(member->channel, "presence_id");
const char *chat_proto = switch_channel_get_variable(member->channel, "chat_proto");
switch_event_t *reply = NULL;
if (presence_id && chat_proto) {
if (switch_event_get_header(processed, presence_id)) {
continue;
}
switch_event_dup(&reply, event);
switch_event_add_header_string(reply, SWITCH_STACK_BOTTOM, "to", presence_id);
switch_event_add_header_string(reply, SWITCH_STACK_BOTTOM, "conference_name", conference->name);
switch_event_add_header_string(reply, SWITCH_STACK_BOTTOM, "conference_domain", conference->domain);
switch_event_set_body(reply, switch_event_get_body(event));
switch_core_chat_deliver(chat_proto, &reply);
switch_event_add_header_string(processed, SWITCH_STACK_BOTTOM, presence_id, "true");
}
}
}
switch_event_destroy(&processed);
switch_mutex_unlock(conference->member_mutex);
}
void call_setup_event_handler(switch_event_t *event)
{
switch_status_t status = SWITCH_STATUS_FALSE;
conference_obj_t *conference = NULL;
char *conf = switch_event_get_header(event, "Target-Component");
char *domain = switch_event_get_header(event, "Target-Domain");
char *dial_str = switch_event_get_header(event, "Request-Target");
char *dial_uri = switch_event_get_header(event, "Request-Target-URI");
char *action = switch_event_get_header(event, "Request-Action");
char *ext = switch_event_get_header(event, "Request-Target-Extension");
char *ext_domain = switch_event_get_header(event, "Request-Target-Domain");
char *full_url = switch_event_get_header(event, "full_url");
char *call_id = switch_event_get_header(event, "Request-Call-ID");
if (!ext) ext = dial_str;
if (!zstr(conf) && !zstr(dial_str) && !zstr(action) && (conference = conference_find(conf, domain))) {
switch_event_t *var_event;
switch_event_header_t *hp;
if (conference_test_flag(conference, CFLAG_RFC4579)) {
char *key = switch_mprintf("conf_%s_%s_%s_%s", conference->name, conference->domain, ext, ext_domain);
char *expanded = NULL, *ostr = dial_str;;
if (!strcasecmp(action, "call")) {
if((conference->max_members > 0) && (conference->count >= conference->max_members)) {
// Conference member limit has been reached; do not proceed with setup request
status = SWITCH_STATUS_FALSE;
} else {
if (switch_event_create_plain(&var_event, SWITCH_EVENT_CHANNEL_DATA) != SWITCH_STATUS_SUCCESS) {
abort();
}
for(hp = event->headers; hp; hp = hp->next) {
if (!strncasecmp(hp->name, "var_", 4)) {
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, hp->name + 4, hp->value);
}
}
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "conference_call_key", key);
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "conference_destination_number", ext);
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "conference_invite_uri", dial_uri);
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "conference_track_status", "true");
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "conference_track_call_id", call_id);
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "sip_invite_domain", domain);
switch_event_add_header_string(var_event, SWITCH_STACK_BOTTOM, "sip_invite_contact_params", "~isfocus");
if (!strncasecmp(ostr, "url+", 4)) {
ostr += 4;
} else if (!switch_true(full_url) && conference->outcall_templ) {
if ((expanded = switch_event_expand_headers(var_event, conference->outcall_templ))) {
ostr = expanded;
}
}
status = conference_outcall_bg(conference, NULL, NULL, ostr, 60, NULL, NULL, NULL, NULL, NULL, NULL, &var_event);
if (expanded && expanded != conference->outcall_templ) {
switch_safe_free(expanded);
}
}
} else if (!strcasecmp(action, "end")) {
if (switch_core_session_hupall_matching_var("conference_call_key", key, SWITCH_CAUSE_NORMAL_CLEARING)) {
send_conference_notify(conference, "SIP/2.0 200 OK\r\n", call_id, SWITCH_TRUE);
} else {
send_conference_notify(conference, "SIP/2.0 481 Failure\r\n", call_id, SWITCH_TRUE);
}
status = SWITCH_STATUS_SUCCESS;
}
switch_safe_free(key);
} else { // Conference found but doesn't support referral.
status = SWITCH_STATUS_FALSE;
}
switch_thread_rwlock_unlock(conference->rwlock);
} else { // Couldn't find associated conference. Indicate failure on refer subscription
status = SWITCH_STATUS_FALSE;
}
if(status != SWITCH_STATUS_SUCCESS) {
// Unable to setup call, need to generate final NOTIFY
if (switch_event_create(&event, SWITCH_EVENT_CONFERENCE_DATA) == SWITCH_STATUS_SUCCESS) {
event->flags |= EF_UNIQ_HEADERS;
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "conference-name", conf);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "conference-domain", domain);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "conference-event", "refer");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call_id", call_id);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "final", "true");
switch_event_add_body(event, "%s", "SIP/2.0 481 Failure\r\n");
switch_event_fire(&event);
}
}
}
void conf_data_event_handler(switch_event_t *event)
{
switch_event_t *revent;
char *name = switch_event_get_header(event, "conference-name");
char *domain = switch_event_get_header(event, "conference-domain");
conference_obj_t *conference = NULL;
char *body = NULL;
if (!zstr(name) && (conference = conference_find(name, domain))) {
if (conference_test_flag(conference, CFLAG_RFC4579)) {
switch_event_dup(&revent, event);
revent->event_id = SWITCH_EVENT_CONFERENCE_DATA;
revent->flags |= EF_UNIQ_HEADERS;
switch_event_add_header(revent, SWITCH_STACK_TOP, "Event-Name", "CONFERENCE_DATA");
body = conference_rfc4579_render(conference, event, revent);
switch_event_add_body(revent, "%s", body);
switch_event_fire(&revent);
switch_safe_free(body);
}
switch_thread_rwlock_unlock(conference->rwlock);
}
}
void pres_event_handler(switch_event_t *event)
{
char *to = switch_event_get_header(event, "to");
char *domain_name = NULL;
char *dup_to = NULL, *conf_name, *dup_conf_name = NULL;
conference_obj_t *conference;
if (!to || strncasecmp(to, "conf+", 5) || !strchr(to, '@')) {
return;
}
if (!(dup_to = strdup(to))) {
return;
}
conf_name = dup_to + 5;
if ((domain_name = strchr(conf_name, '@'))) {
*domain_name++ = '\0';
}
dup_conf_name = switch_mprintf("%q@%q", conf_name, domain_name);
if ((conference = conference_find(conf_name, NULL)) || (conference = conference_find(dup_conf_name, NULL))) {
if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", CONF_CHAT_PROTO);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", conference->name);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", conference->name, conference->domain);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "force-status", "Active (%d caller%s)", conference->count, conference->count == 1 ? "" : "s");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", conf_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", conference->count == 1 ? "early" : "confirmed");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-direction", conference->count == 1 ? "outbound" : "inbound");
switch_event_fire(&event);
}
switch_thread_rwlock_unlock(conference->rwlock);
} else if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", CONF_CHAT_PROTO);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", conf_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", to);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Idle");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", conf_name);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-direction", "inbound");
switch_event_fire(&event);
}
switch_safe_free(dup_to);
switch_safe_free(dup_conf_name);
}
switch_status_t chat_send(switch_event_t *message_event)
{
char name[512] = "", *p, *lbuf = NULL;
conference_obj_t *conference = NULL;
switch_stream_handle_t stream = { 0 };
const char *proto;
const char *from;
const char *to;
//const char *subject;
const char *body;
//const char *type;
const char *hint;
proto = switch_event_get_header(message_event, "proto");
from = switch_event_get_header(message_event, "from");
to = switch_event_get_header(message_event, "to");
body = switch_event_get_body(message_event);
hint = switch_event_get_header(message_event, "hint");
if ((p = strchr(to, '+'))) {
to = ++p;
}
if (!body) {
return SWITCH_STATUS_SUCCESS;
}
if ((p = strchr(to, '@'))) {
switch_copy_string(name, to, ++p - to);
} else {
switch_copy_string(name, to, sizeof(name));
}
if (!(conference = conference_find(name, NULL))) {
switch_core_chat_send_args(proto, CONF_CHAT_PROTO, to, hint && strchr(hint, '/') ? hint : from, "",
"Conference not active.", NULL, NULL, SWITCH_FALSE);
return SWITCH_STATUS_FALSE;
}
SWITCH_STANDARD_STREAM(stream);
if (body != NULL && (lbuf = strdup(body))) {
/* special case list */
if (conference->broadcast_chat_messages) {
chat_message_broadcast(conference, message_event);
} else if (switch_stristr("list", lbuf)) {
conference_list_pretty(conference, &stream);
/* provide help */
} else {
return SWITCH_STATUS_SUCCESS;
}
}
switch_safe_free(lbuf);
if (!conference->broadcast_chat_messages) {
switch_core_chat_send_args(proto, CONF_CHAT_PROTO, to, hint && strchr(hint, '/') ? hint : from, "", stream.data, NULL, NULL, SWITCH_FALSE);
}
switch_safe_free(stream.data);
switch_thread_rwlock_unlock(conference->rwlock);
return SWITCH_STATUS_SUCCESS;
}

View File

@ -0,0 +1,392 @@
/*
* 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/
*
* 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):
*
* 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>
switch_status_t conference_file_close(conference_obj_t *conference, conference_file_node_t *node)
{
switch_event_t *event;
conference_member_t *member = NULL;
if (test_eflag(conference, EFLAG_PLAY_FILE_DONE) &&
switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
conference_add_event_data(conference, event);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "seconds", "%ld", (long) node->fh.samples_in / node->fh.native_rate);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "milliseconds", "%ld", (long) node->fh.samples_in / (node->fh.native_rate / 1000));
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "samples", "%ld", (long) node->fh.samples_in);
if (node->fh.params) {
switch_event_merge(event, node->fh.params);
}
if (node->member_id) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "play-file-member-done");
if ((member = conference_member_get(conference, node->member_id))) {
conference_add_event_member_data(member, event);
switch_thread_rwlock_unlock(member->rwlock);
}
} else {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "play-file-done");
}
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "File", node->file);
if (node->async) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Async", "true");
}
switch_event_fire(&event);
}
#ifdef OPENAL_POSITIONING
if (node->al && node->al->device) {
close_al(node->al);
}
#endif
if (switch_core_file_has_video(&node->fh) && conference->canvas) {
conference->canvas->timer.interval = conference->video_fps.ms;
conference->canvas->timer.samples = conference->video_fps.samples;
switch_core_timer_sync(&conference->canvas->timer);
conference->canvas->send_keyframe = 1;
conference->playing_video_file = 0;
}
return switch_core_file_close(&node->fh);
}
/* Make files stop playing in a conference either the current one or all of them */
uint32_t conference_stop_file(conference_obj_t *conference, file_stop_t stop)
{
uint32_t count = 0;
conference_file_node_t *nptr;
switch_assert(conference != NULL);
switch_mutex_lock(conference->mutex);
if (stop == FILE_STOP_ALL) {
for (nptr = conference->fnode; nptr; nptr = nptr->next) {
nptr->done++;
count++;
}
if (conference->async_fnode) {
conference->async_fnode->done++;
count++;
}
} else if (stop == FILE_STOP_ASYNC) {
if (conference->async_fnode) {
conference->async_fnode->done++;
count++;
}
} else {
if (conference->fnode) {
conference->fnode->done++;
count++;
}
}
switch_mutex_unlock(conference->mutex);
return count;
}
/* Play a file in the conference room */
switch_status_t conference_play_file(conference_obj_t *conference, char *file, uint32_t leadin, switch_channel_t *channel, uint8_t async)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
conference_file_node_t *fnode, *nptr = NULL;
switch_memory_pool_t *pool;
uint32_t count;
char *dfile = NULL, *expanded = NULL;
int say = 0;
uint8_t channels = (uint8_t) conference->channels;
int bad_params = 0;
int flags = 0;
switch_assert(conference != NULL);
if (zstr(file)) {
return SWITCH_STATUS_NOTFOUND;
}
switch_mutex_lock(conference->mutex);
switch_mutex_lock(conference->member_mutex);
count = conference->count;
switch_mutex_unlock(conference->member_mutex);
switch_mutex_unlock(conference->mutex);
if (!count) {
return SWITCH_STATUS_FALSE;
}
if (channel) {
if ((expanded = switch_channel_expand_variables(channel, file)) != file) {
file = expanded;
} else {
expanded = NULL;
}
}
if (!strncasecmp(file, "say:", 4)) {
say = 1;
}
if (!async && say) {
status = conference_say(conference, file + 4, leadin);
goto done;
}
if (!switch_is_file_path(file)) {
if (!say && conference->sound_prefix) {
char *params_portion = NULL;
char *file_portion = NULL;
switch_separate_file_params(file, &file_portion, &params_portion);
if (params_portion) {
dfile = switch_mprintf("%s%s%s%s", params_portion, conference->sound_prefix, SWITCH_PATH_SEPARATOR, file_portion);
} else {
dfile = switch_mprintf("%s%s%s", conference->sound_prefix, SWITCH_PATH_SEPARATOR, file_portion);
}
file = dfile;
switch_safe_free(file_portion);
switch_safe_free(params_portion);
} else if (!async) {
status = conference_say(conference, file, leadin);
goto done;
} else {
goto done;
}
}
/* Setup a memory pool to use. */
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, 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_LOG, SWITCH_LOG_CRIT, "Alloc Failure\n");
switch_core_destroy_memory_pool(&pool);
status = SWITCH_STATUS_MEMERR;
goto done;
}
fnode->conference = conference;
fnode->layer_id = -1;
fnode->type = NODE_TYPE_FILE;
fnode->leadin = leadin;
if (switch_stristr("position=", file)) {
/* positional requires mono input */
fnode->fh.channels = channels = 1;
}
retry:
flags = SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT;
if (conference->members_with_video && conference_test_flag(conference, CFLAG_TRANSCODE_VIDEO)) {
flags |= SWITCH_FILE_FLAG_VIDEO;
}
/* Open the file */
fnode->fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN;
if (switch_core_file_open(&fnode->fh, file, channels, conference->rate, flags, pool) != SWITCH_STATUS_SUCCESS) {
switch_event_t *event;
if (test_eflag(conference, EFLAG_PLAY_FILE) &&
switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
conference_add_event_data(conference, event);
if (fnode->fh.params) {
switch_event_merge(event, conference->fnode->fh.params);
}
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "play-file");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "File", file);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Async", async ? "true" : "false");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Error", "File could not be played");
switch_event_fire(&event);
}
switch_core_destroy_memory_pool(&pool);
status = SWITCH_STATUS_NOTFOUND;
goto done;
}
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");
if (!zstr(vol)) {
fnode->fh.vol = atoi(vol);
}
if (!bad_params && !zstr(position) && conference->channels == 2) {
fnode->al = create_al(pool);
if (parse_position(fnode->al, position) != SWITCH_STATUS_SUCCESS) {
switch_core_file_close(&fnode->fh);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Position Data.\n");
fnode->al = NULL;
channels = (uint8_t)conference->channels;
bad_params = 1;
goto retry;
}
}
}
fnode->pool = pool;
fnode->async = async;
fnode->file = switch_core_strdup(fnode->pool, file);
if (!conference->fnode || (async && !conference->async_fnode)) {
fnode_check_video(fnode);
}
/* Queue the node */
switch_mutex_lock(conference->mutex);
if (async) {
if (conference->async_fnode) {
nptr = conference->async_fnode;
}
conference->async_fnode = fnode;
if (nptr) {
switch_memory_pool_t *tmppool;
conference_file_close(conference, nptr);
tmppool = nptr->pool;
switch_core_destroy_memory_pool(&tmppool);
}
} else {
for (nptr = conference->fnode; nptr && nptr->next; nptr = nptr->next);
if (nptr) {
nptr->next = fnode;
} else {
conference->fnode = fnode;
}
}
switch_mutex_unlock(conference->mutex);
done:
switch_safe_free(expanded);
switch_safe_free(dfile);
return status;
}
/* Play a file */
switch_status_t conference_local_play_file(conference_obj_t *conference, switch_core_session_t *session, char *path, uint32_t leadin, void *buf,
uint32_t buflen)
{
uint32_t x = 0;
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_channel_t *channel;
char *expanded = NULL;
switch_input_args_t args = { 0 }, *ap = NULL;
if (buf) {
args.buf = buf;
args.buflen = buflen;
ap = &args;
}
/* generate some space infront of the file to be played */
for (x = 0; x < leadin; x++) {
switch_frame_t *read_frame;
status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
if (!SWITCH_READ_ACCEPTABLE(status)) {
break;
}
}
/* if all is well, really play the file */
if (status == SWITCH_STATUS_SUCCESS) {
char *dpath = NULL;
channel = switch_core_session_get_channel(session);
if ((expanded = switch_channel_expand_variables(channel, path)) != path) {
path = expanded;
} else {
expanded = NULL;
}
if (!strncasecmp(path, "say:", 4)) {
if (!(conference->tts_engine && conference->tts_voice)) {
status = SWITCH_STATUS_FALSE;
} else {
status = switch_ivr_speak_text(session, conference->tts_engine, conference->tts_voice, path + 4, ap);
}
goto done;
}
if (!switch_is_file_path(path) && conference->sound_prefix) {
if (!(dpath = switch_mprintf("%s%s%s", conference->sound_prefix, SWITCH_PATH_SEPARATOR, path))) {
status = SWITCH_STATUS_MEMERR;
goto done;
}
path = dpath;
}
status = switch_ivr_play_file(session, NULL, path, ap);
switch_safe_free(dpath);
}
done:
switch_safe_free(expanded);
return status;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,388 @@
/*
* 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/
*
* 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):
*
* 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>
/* stop the specified recording */
switch_status_t conference_record_stop(conference_obj_t *conference, switch_stream_handle_t *stream, char *path)
{
conference_member_t *member = NULL;
int count = 0;
switch_assert(conference != NULL);
switch_mutex_lock(conference->member_mutex);
for (member = conference->members; member; member = member->next) {
if (member_test_flag(member, MFLAG_NOCHANNEL) && (!path || !strcmp(path, member->rec_path))) {
if (!conference_test_flag(conference, CFLAG_CONF_RESTART_AUTO_RECORD) && member->rec && member->rec->autorec) {
stream->write_function(stream, "Stopped AUTO recording file %s (Auto Recording Now Disabled)\n", member->rec_path);
conference->auto_record = 0;
} else {
stream->write_function(stream, "Stopped recording file %s\n", member->rec_path);
}
member_clear_flag_locked(member, MFLAG_RUNNING);
count++;
}
}
conference->record_count -= count;
switch_mutex_unlock(conference->member_mutex);
return count;
}
/* stop/pause/resume the specified recording */
switch_status_t conference_record_action(conference_obj_t *conference, char *path, recording_action_type_t action)
{
conference_member_t *member = NULL;
int count = 0;
//switch_file_handle_t *fh = NULL;
switch_assert(conference != NULL);
switch_mutex_lock(conference->member_mutex);
for (member = conference->members; member; member = member->next)
{
if (member_test_flag(member, MFLAG_NOCHANNEL) && (!path || !strcmp(path, member->rec_path)))
{
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Action: %d\n", action);
switch (action)
{
case REC_ACTION_STOP:
member_clear_flag_locked(member, MFLAG_RUNNING);
count++;
break;
case REC_ACTION_PAUSE:
member_set_flag_locked(member, MFLAG_PAUSE_RECORDING);
count = 1;
break;
case REC_ACTION_RESUME:
member_clear_flag_locked(member, MFLAG_PAUSE_RECORDING);
count = 1;
break;
}
}
}
switch_mutex_unlock(conference->member_mutex);
return count;
}
/* Sub-Routine called by a record entity inside a conference */
void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *thread, void *obj)
{
int16_t *data_buf;
conference_member_t smember = { 0 }, *member;
conference_record_t *rp, *last = NULL, *rec = (conference_record_t *) obj;
conference_obj_t *conference = rec->conference;
uint32_t samples = switch_samples_per_packet(conference->rate, conference->interval);
uint32_t mux_used;
char *vval;
switch_timer_t timer = { 0 };
uint32_t rlen;
switch_size_t data_buf_len;
switch_event_t *event;
switch_size_t len = 0;
int flags = 0;
data_buf_len = samples * sizeof(int16_t);
switch_zmalloc(data_buf, data_buf_len);
if (switch_thread_rwlock_tryrdlock(conference->rwlock) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Read Lock Fail\n");
return NULL;
}
data_buf_len = samples * sizeof(int16_t) * conference->channels;
switch_zmalloc(data_buf, data_buf_len);
switch_mutex_lock(mod_conference_globals.hash_mutex);
mod_conference_globals.threads++;
switch_mutex_unlock(mod_conference_globals.hash_mutex);
member = &smember;
member->flags[MFLAG_CAN_HEAR] = member->flags[MFLAG_NOCHANNEL] = member->flags[MFLAG_RUNNING] = 1;
member->conference = conference;
member->native_rate = conference->rate;
member->rec = rec;
member->rec_path = rec->path;
member->rec_time = switch_epoch_time_now(NULL);
member->rec->fh.channels = 1;
member->rec->fh.samplerate = conference->rate;
member->id = next_member_id();
member->pool = rec->pool;
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);
switch_mutex_init(&member->write_mutex, SWITCH_MUTEX_NESTED, rec->pool);
switch_mutex_init(&member->flag_mutex, SWITCH_MUTEX_NESTED, rec->pool);
switch_mutex_init(&member->fnode_mutex, SWITCH_MUTEX_NESTED, rec->pool);
switch_mutex_init(&member->audio_in_mutex, SWITCH_MUTEX_NESTED, rec->pool);
switch_mutex_init(&member->audio_out_mutex, SWITCH_MUTEX_NESTED, rec->pool);
switch_mutex_init(&member->read_mutex, SWITCH_MUTEX_NESTED, rec->pool);
switch_thread_rwlock_create(&member->rwlock, rec->pool);
/* Setup an audio buffer for the incoming audio */
if (switch_buffer_create_dynamic(&member->audio_buffer, CONF_DBLOCK_SIZE, CONF_DBUFFER_SIZE, 0) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error Creating Audio Buffer!\n");
goto end;
}
/* Setup an audio buffer for the outgoing audio */
if (switch_buffer_create_dynamic(&member->mux_buffer, CONF_DBLOCK_SIZE, CONF_DBUFFER_SIZE, 0) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error Creating Audio Buffer!\n");
goto end;
}
if (conference->canvas) {
conference->canvas->send_keyframe = 1;
}
member->rec->fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN;
flags = SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT;
if (conference->members_with_video && conference_test_flag(conference, CFLAG_TRANSCODE_VIDEO)) {
flags |= SWITCH_FILE_FLAG_VIDEO;
if (conference->canvas) {
char *orig_path = rec->path;
rec->path = switch_core_sprintf(rec->pool, "{channels=%d,samplerate=%d,vw=%d,vh=%d,fps=%0.2f}%s",
conference->channels,
conference->rate,
conference->canvas->width,
conference->canvas->height,
conference->video_fps.fps,
orig_path);
}
}
if (switch_core_file_open(&member->rec->fh, rec->path, (uint8_t) conference->channels, conference->rate, flags, rec->pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening File [%s]\n", rec->path);
if (test_eflag(conference, EFLAG_RECORD) &&
switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
conference_add_event_data(conference, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "start-recording");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Path", rec->path);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Error", "File could not be opened for recording");
switch_event_fire(&event);
}
goto end;
}
switch_mutex_lock(conference->mutex);
if (conference->video_floor_holder) {
conference_member_t *member;
if ((member = conference_member_get(conference, conference->video_floor_holder))) {
if (member->session) {
switch_core_session_video_reinit(member->session);
}
switch_thread_rwlock_unlock(member->rwlock);
}
}
switch_mutex_unlock(conference->mutex);
if (switch_core_timer_init(&timer, conference->timer_name, conference->interval, samples, rec->pool) == SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Setup timer success interval: %u samples: %u\n", conference->interval, samples);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Timer Setup Failed. Conference Cannot Start\n");
goto end;
}
if ((vval = switch_mprintf("Conference %s", conference->name))) {
switch_core_file_set_string(&member->rec->fh, SWITCH_AUDIO_COL_STR_TITLE, vval);
switch_safe_free(vval);
}
switch_core_file_set_string(&member->rec->fh, SWITCH_AUDIO_COL_STR_ARTIST, "FreeSWITCH mod_conference Software Conference Module");
if (test_eflag(conference, EFLAG_RECORD) &&
switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
conference_add_event_data(conference, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "start-recording");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Path", rec->path);
switch_event_fire(&event);
}
if (conference_add_member(conference, member) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Joining Conference\n");
goto end;
}
while (member_test_flag(member, MFLAG_RUNNING) && conference_test_flag(conference, CFLAG_RUNNING) && (conference->count + conference->count_ghosts)) {
len = 0;
mux_used = (uint32_t) switch_buffer_inuse(member->mux_buffer);
if (member_test_flag(member, MFLAG_FLUSH_BUFFER)) {
if (mux_used) {
switch_mutex_lock(member->audio_out_mutex);
switch_buffer_zero(member->mux_buffer);
switch_mutex_unlock(member->audio_out_mutex);
mux_used = 0;
}
member_clear_flag_locked(member, MFLAG_FLUSH_BUFFER);
}
again:
if (switch_test_flag((&member->rec->fh), SWITCH_FILE_PAUSE)) {
member_set_flag_locked(member, MFLAG_FLUSH_BUFFER);
goto loop;
}
if (mux_used >= data_buf_len) {
/* Flush the output buffer and write all the data (presumably muxed) to the file */
switch_mutex_lock(member->audio_out_mutex);
//low_count = 0;
if ((rlen = (uint32_t) switch_buffer_read(member->mux_buffer, data_buf, data_buf_len))) {
len = (switch_size_t) rlen / sizeof(int16_t) / conference->channels;
}
switch_mutex_unlock(member->audio_out_mutex);
}
if (len == 0) {
mux_used = (uint32_t) switch_buffer_inuse(member->mux_buffer);
if (mux_used >= data_buf_len) {
goto again;
}
memset(data_buf, 255, (switch_size_t) data_buf_len);
len = (switch_size_t) samples;
}
if (!member_test_flag(member, MFLAG_PAUSE_RECORDING)) {
if (!len || switch_core_file_write(&member->rec->fh, data_buf, &len) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write Failed\n");
member_clear_flag_locked(member, MFLAG_RUNNING);
}
}
loop:
switch_core_timer_next(&timer);
} /* Rinse ... Repeat */
end:
for(;;) {
switch_mutex_lock(member->audio_out_mutex);
rlen = (uint32_t) switch_buffer_read(member->mux_buffer, data_buf, data_buf_len);
switch_mutex_unlock(member->audio_out_mutex);
if (rlen > 0) {
len = (switch_size_t) rlen / sizeof(int16_t)/ conference->channels;
switch_core_file_write(&member->rec->fh, data_buf, &len);
} else {
break;
}
}
switch_safe_free(data_buf);
switch_core_timer_destroy(&timer);
conference_del_member(conference, member);
if (conference->canvas) {
conference->canvas->send_keyframe = 1;
}
switch_buffer_destroy(&member->audio_buffer);
switch_buffer_destroy(&member->mux_buffer);
member_clear_flag_locked(member, MFLAG_RUNNING);
if (switch_test_flag((&member->rec->fh), SWITCH_FILE_OPEN)) {
switch_mutex_lock(conference->mutex);
switch_mutex_unlock(conference->mutex);
switch_core_file_close(&member->rec->fh);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Recording of %s Stopped\n", rec->path);
if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
conference_add_event_data(conference, event);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "stop-recording");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Path", rec->path);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Samples-Out", "%ld", (long) member->rec->fh.samples_out);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Samplerate", "%ld", (long) member->rec->fh.samplerate);
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Milliseconds-Elapsed", "%ld", (long) member->rec->fh.samples_out / (member->rec->fh.samplerate / 1000));
switch_event_fire(&event);
}
if (rec->autorec && conference->auto_recording) {
conference->auto_recording--;
}
switch_mutex_lock(conference->flag_mutex);
for (rp = conference->rec_node_head; rp; rp = rp->next) {
if (rec == rp) {
if (last) {
last->next = rp->next;
} else {
conference->rec_node_head = rp->next;
}
}
}
switch_mutex_unlock(conference->flag_mutex);
if (rec->pool) {
switch_memory_pool_t *pool = rec->pool;
rec = NULL;
switch_core_destroy_memory_pool(&pool);
}
switch_mutex_lock(mod_conference_globals.hash_mutex);
mod_conference_globals.threads--;
switch_mutex_unlock(mod_conference_globals.hash_mutex);
switch_thread_rwlock_unlock(conference->rwlock);
return NULL;
}

View File

@ -0,0 +1,316 @@
/*
* 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/
*
* 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):
*
* 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>
void set_mflags(const char *flags, member_flag_t *f)
{
if (flags) {
char *dup = strdup(flags);
char *p;
char *argv[10] = { 0 };
int i, argc = 0;
f[MFLAG_CAN_SPEAK] = f[MFLAG_CAN_HEAR] = f[MFLAG_CAN_BE_SEEN] = 1;
for (p = dup; p && *p; p++) {
if (*p == ',') {
*p = '|';
}
}
argc = switch_separate_string(dup, '|', argv, (sizeof(argv) / sizeof(argv[0])));
for (i = 0; i < argc && argv[i]; i++) {
if (!strcasecmp(argv[i], "mute")) {
f[MFLAG_CAN_SPEAK] = 0;
f[MFLAG_TALKING] = 0;
} else if (!strcasecmp(argv[i], "deaf")) {
f[MFLAG_CAN_HEAR] = 0;
} else if (!strcasecmp(argv[i], "mute-detect")) {
f[MFLAG_MUTE_DETECT] = 1;
} else if (!strcasecmp(argv[i], "dist-dtmf")) {
f[MFLAG_DIST_DTMF] = 1;
} else if (!strcasecmp(argv[i], "moderator")) {
f[MFLAG_MOD] = 1;
} else if (!strcasecmp(argv[i], "nomoh")) {
f[MFLAG_NOMOH] = 1;
} else if (!strcasecmp(argv[i], "endconf")) {
f[MFLAG_ENDCONF] = 1;
} else if (!strcasecmp(argv[i], "mintwo")) {
f[MFLAG_MINTWO] = 1;
} else if (!strcasecmp(argv[i], "video-bridge")) {
f[MFLAG_VIDEO_BRIDGE] = 1;
} else if (!strcasecmp(argv[i], "ghost")) {
f[MFLAG_GHOST] = 1;
} else if (!strcasecmp(argv[i], "join-only")) {
f[MFLAG_JOIN_ONLY] = 1;
} else if (!strcasecmp(argv[i], "positional")) {
f[MFLAG_POSITIONAL] = 1;
} else if (!strcasecmp(argv[i], "no-positional")) {
f[MFLAG_NO_POSITIONAL] = 1;
} else if (!strcasecmp(argv[i], "join-vid-floor")) {
f[MFLAG_JOIN_VID_FLOOR] = 1;
} else if (!strcasecmp(argv[i], "no-minimize-encoding")) {
f[MFLAG_NO_MINIMIZE_ENCODING] = 1;
} else if (!strcasecmp(argv[i], "second-screen")) {
f[MFLAG_SECOND_SCREEN] = 1;
f[MFLAG_CAN_SPEAK] = 0;
f[MFLAG_TALKING] = 0;
f[MFLAG_CAN_HEAR] = 0;
f[MFLAG_SILENT] = 1;
}
}
free(dup);
}
}
void set_cflags(const char *flags, conference_flag_t *f)
{
if (flags) {
char *dup = strdup(flags);
char *p;
char *argv[10] = { 0 };
int i, argc = 0;
for (p = dup; p && *p; p++) {
if (*p == ',') {
*p = '|';
}
}
argc = switch_separate_string(dup, '|', argv, (sizeof(argv) / sizeof(argv[0])));
for (i = 0; i < argc && argv[i]; i++) {
if (!strcasecmp(argv[i], "wait-mod")) {
f[CFLAG_WAIT_MOD] = 1;
} else if (!strcasecmp(argv[i], "video-floor-only")) {
f[CFLAG_VID_FLOOR] = 1;
} else if (!strcasecmp(argv[i], "audio-always")) {
f[CFLAG_AUDIO_ALWAYS] = 1;
} else if (!strcasecmp(argv[i], "restart-auto-record")) {
f[CFLAG_CONF_RESTART_AUTO_RECORD] = 1;
} else if (!strcasecmp(argv[i], "json-events")) {
f[CFLAG_JSON_EVENTS] = 1;
} else if (!strcasecmp(argv[i], "livearray-sync")) {
f[CFLAG_LIVEARRAY_SYNC] = 1;
} else if (!strcasecmp(argv[i], "livearray-json-status")) {
f[CFLAG_JSON_STATUS] = 1;
} else if (!strcasecmp(argv[i], "rfc-4579")) {
f[CFLAG_RFC4579] = 1;
} else if (!strcasecmp(argv[i], "auto-3d-position")) {
f[CFLAG_POSITIONAL] = 1;
} else if (!strcasecmp(argv[i], "minimize-video-encoding")) {
f[CFLAG_MINIMIZE_VIDEO_ENCODING] = 1;
} else if (!strcasecmp(argv[i], "video-bridge-first-two")) {
f[CFLAG_VIDEO_BRIDGE_FIRST_TWO] = 1;
} else if (!strcasecmp(argv[i], "video-required-for-canvas")) {
f[CFLAG_VIDEO_REQUIRED_FOR_CANVAS] = 1;
} else if (!strcasecmp(argv[i], "manage-inbound-video-bitrate")) {
f[CFLAG_MANAGE_INBOUND_VIDEO_BITRATE] = 1;
} else if (!strcasecmp(argv[i], "video-muxing-personal-canvas")) {
f[CFLAG_PERSONAL_CANVAS] = 1;
}
}
free(dup);
}
}
void clear_eflags(char *events, uint32_t *f)
{
char buf[512] = "";
char *next = NULL;
char *event = buf;
if (events) {
switch_copy_string(buf, events, sizeof(buf));
while (event) {
next = strchr(event, ',');
if (next) {
*next++ = '\0';
}
if (!strcmp(event, "add-member")) {
*f &= ~EFLAG_ADD_MEMBER;
} else if (!strcmp(event, "del-member")) {
*f &= ~EFLAG_DEL_MEMBER;
} else if (!strcmp(event, "energy-level")) {
*f &= ~EFLAG_ENERGY_LEVEL;
} else if (!strcmp(event, "volume-level")) {
*f &= ~EFLAG_VOLUME_LEVEL;
} else if (!strcmp(event, "gain-level")) {
*f &= ~EFLAG_GAIN_LEVEL;
} else if (!strcmp(event, "dtmf")) {
*f &= ~EFLAG_DTMF;
} else if (!strcmp(event, "stop-talking")) {
*f &= ~EFLAG_STOP_TALKING;
} else if (!strcmp(event, "start-talking")) {
*f &= ~EFLAG_START_TALKING;
} else if (!strcmp(event, "mute-detect")) {
*f &= ~EFLAG_MUTE_DETECT;
} else if (!strcmp(event, "mute-member")) {
*f &= ~EFLAG_MUTE_MEMBER;
} else if (!strcmp(event, "unmute-member")) {
*f &= ~EFLAG_UNMUTE_MEMBER;
} else if (!strcmp(event, "kick-member")) {
*f &= ~EFLAG_KICK_MEMBER;
} else if (!strcmp(event, "dtmf-member")) {
*f &= ~EFLAG_DTMF_MEMBER;
} else if (!strcmp(event, "energy-level-member")) {
*f &= ~EFLAG_ENERGY_LEVEL_MEMBER;
} else if (!strcmp(event, "volume-in-member")) {
*f &= ~EFLAG_VOLUME_IN_MEMBER;
} else if (!strcmp(event, "volume-out-member")) {
*f &= ~EFLAG_VOLUME_OUT_MEMBER;
} else if (!strcmp(event, "play-file")) {
*f &= ~EFLAG_PLAY_FILE;
} else if (!strcmp(event, "play-file-done")) {
*f &= ~EFLAG_PLAY_FILE_DONE;
} else if (!strcmp(event, "play-file-member")) {
*f &= ~EFLAG_PLAY_FILE_MEMBER;
} else if (!strcmp(event, "speak-text")) {
*f &= ~EFLAG_SPEAK_TEXT;
} else if (!strcmp(event, "speak-text-member")) {
*f &= ~EFLAG_SPEAK_TEXT_MEMBER;
} else if (!strcmp(event, "lock")) {
*f &= ~EFLAG_LOCK;
} else if (!strcmp(event, "unlock")) {
*f &= ~EFLAG_UNLOCK;
} else if (!strcmp(event, "transfer")) {
*f &= ~EFLAG_TRANSFER;
} else if (!strcmp(event, "bgdial-result")) {
*f &= ~EFLAG_BGDIAL_RESULT;
} else if (!strcmp(event, "floor-change")) {
*f &= ~EFLAG_FLOOR_CHANGE;
} else if (!strcmp(event, "record")) {
*f &= ~EFLAG_RECORD;
}
event = next;
}
}
}
void merge_mflags(member_flag_t *a, member_flag_t *b)
{
int x;
for (x = 0; x < MFLAG_MAX; x++) {
if (b[x]) a[x] = 1;
}
}
void conference_set_flag(conference_obj_t *conference, conference_flag_t flag)
{
conference->flags[flag] = 1;
}
void conference_set_flag_locked(conference_obj_t *conference, conference_flag_t flag)
{
switch_mutex_lock(conference->flag_mutex);
conference->flags[flag] = 1;
switch_mutex_unlock(conference->flag_mutex);
}
void conference_clear_flag(conference_obj_t *conference, conference_flag_t flag)
{
conference->flags[flag] = 0;
}
void conference_clear_flag_locked(conference_obj_t *conference, conference_flag_t flag)
{
switch_mutex_lock(conference->flag_mutex);
conference->flags[flag] = 0;
switch_mutex_unlock(conference->flag_mutex);
}
switch_bool_t conference_test_flag(conference_obj_t *conference, conference_flag_t flag)
{
return !!conference->flags[flag];
}
#if 0
void conference_set_mflag(conference_obj_t *conference, member_flag_t mflag)
{
conference->mflags[mflag] = 1;
}
void conference_clear_mflag(conference_obj_t *conference, member_flag_t mflag)
{
conference->mflags[mflag] = 0;
}
switch_bool_t conference_test_mflag(conference_obj_t *conference, member_flag_t mflag)
{
return !!conference->mflags[mflag];
}
#endif
void member_set_flag(conference_member_t *member, member_flag_t flag)
{
member->flags[flag] = 1;
}
void member_set_flag_locked(conference_member_t *member, member_flag_t flag)
{
switch_mutex_lock(member->flag_mutex);
member->flags[flag] = 1;
switch_mutex_unlock(member->flag_mutex);
}
void member_clear_flag(conference_member_t *member, member_flag_t flag)
{
member->flags[flag] = 0;
}
void member_clear_flag_locked(conference_member_t *member, member_flag_t flag)
{
switch_mutex_lock(member->flag_mutex);
member->flags[flag] = 0;
switch_mutex_unlock(member->flag_mutex);
}
switch_bool_t member_test_flag(conference_member_t *member, member_flag_t flag)
{
return !!member->flags[flag];
}

File diff suppressed because it is too large Load Diff