Merge pull request #353 in FS/freeswitch from mod_conference_refactor to master
* commit '59d1bdae1ed3d4f8564f02cc4989bedccbd98b5c': FS-7845 whitespace and indention FS-7845 File rename to remove mod_* prefix to local module C files. Change function name prefixes from shorthand 'conf' to longer form 'conference'. Also fixes the channel variable names which had been caught in an over zealous perl -pi -e command... FS-7845 Rename functions so that they relate to the file that they are located in. FS-7845 Refactor of mod_conference to improve compilation speed, and logical organization.
This commit is contained in:
commit
73b98ae9bb
|
@ -2,7 +2,8 @@ include $(top_srcdir)/build/modmake.rulesam
|
|||
MODNAME=mod_conference
|
||||
|
||||
mod_LTLIBRARIES = mod_conference.la
|
||||
mod_conference_la_SOURCES = mod_conference.c
|
||||
mod_conference_la_SOURCES = mod_conference.c conference_api.c conference_loop.c conference_al.c conference_cdr.c conference_video.c
|
||||
mod_conference_la_SOURCES += conference_event.c conference_member.c conference_utils.c conference_file.c conference_record.c
|
||||
mod_conference_la_CFLAGS = $(AM_CFLAGS) -I.
|
||||
mod_conference_la_LIBADD = $(switch_builddir)/libfreeswitch.la
|
||||
mod_conference_la_LDFLAGS = -avoid-version -module -no-undefined -shared
|
||||
|
|
|
@ -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
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
/*
|
||||
* 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 *conference_al_create(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 conference_al_gen_arc(conference_obj_t *conference, switch_stream_handle_t *stream)
|
||||
{
|
||||
}
|
||||
void conference_al_process(al_handle_t *al, void *data, switch_size_t datalen, int rate)
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
void conference_al_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 && conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK) && !conference_utils_member_test_flag(member, MFLAG_NO_POSITIONAL)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count < 3) {
|
||||
for (member = conference->members; member; member = member->next) {
|
||||
if (member->channel && !conference_utils_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 || conference_utils_member_test_flag(member, MFLAG_NO_POSITIONAL) || !conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!member->al) {
|
||||
member->al = conference_al_create(member->pool);
|
||||
}
|
||||
conference_utils_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 conference_al_process(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(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(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
|
||||
|
||||
#ifndef OPENAL_POSITIONING
|
||||
switch_status_t conference_al_parse_position(al_handle_t *al, const char *data)
|
||||
{
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
#else
|
||||
switch_status_t conference_al_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
|
||||
|
||||
#ifdef OPENAL_POSITIONING
|
||||
void conference_al_close(al_handle_t *al)
|
||||
{
|
||||
if (!al) return;
|
||||
|
||||
switch_mutex_lock(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(conference_globals.setup_mutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
|
||||
*/
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,772 @@
|
|||
/*
|
||||
* 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 conference_cdr_test_mflag(conference_cdr_node_t *np, member_flag_t mflag)
|
||||
{
|
||||
return !!np->mflags[mflag];
|
||||
}
|
||||
|
||||
const char *conference_cdr_audio_flow(conference_member_t *member)
|
||||
{
|
||||
const char *flow = "sendrecv";
|
||||
|
||||
if (!conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)) {
|
||||
flow = "recvonly";
|
||||
}
|
||||
|
||||
if (member->channel && switch_channel_test_flag(member->channel, CF_HOLD)) {
|
||||
flow = conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK) ? "sendonly" : "inactive";
|
||||
}
|
||||
|
||||
return flow;
|
||||
}
|
||||
|
||||
|
||||
char *conference_cdr_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), "conference_%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, conference_cdr_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_cdr_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_conference_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", conference_cdr_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, conference_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", conference_off++))) {
|
||||
abort();
|
||||
}
|
||||
switch_xml_set_txt_d(x_ptr, conference->name);
|
||||
|
||||
if (!(x_ptr = switch_xml_add_child_d(x_conference, "hostname", conference_off++))) {
|
||||
abort();
|
||||
}
|
||||
switch_xml_set_txt_d(x_ptr, switch_core_get_hostname());
|
||||
|
||||
if (!(x_ptr = switch_xml_add_child_d(x_conference, "rate", conference_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", conference_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", conference_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", conference_off++))) {
|
||||
abort();
|
||||
}
|
||||
switch_xml_set_attr_d(x_ptr, "endconference_forced", conference_utils_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", conference_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", conference_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, conference_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, conference_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, conference_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, conference_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", conference_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);
|
||||
}
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
|
||||
*/
|
|
@ -0,0 +1,714 @@
|
|||
/*
|
||||
* 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_event_mod_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 *conference_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 (conference_name && (p = strchr(conference_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", conference_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", conference_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", conference_name, action, cid, argv[0]);
|
||||
} else if (!strcasecmp(action, "play") || !strcasecmp(action, "stop")) {
|
||||
exec = switch_mprintf("%s %s %s", conference_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", conference_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(conference_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(conference_name, NULL))) {
|
||||
switch_mutex_lock(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(conference_globals.setup_mutex);
|
||||
switch_thread_rwlock_unlock(conference->rwlock);
|
||||
}
|
||||
addobj = array;
|
||||
}
|
||||
|
||||
if (exec) {
|
||||
conference_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__, conference_globals.event_channel_id);
|
||||
|
||||
|
||||
switch_safe_free(stream.data);
|
||||
switch_safe_free(exec);
|
||||
|
||||
switch_safe_free(conference_name);
|
||||
|
||||
}
|
||||
|
||||
void conference_event_la_channel_handler(const char *event_channel, cJSON *json, const char *key, switch_event_channel_id_t id)
|
||||
{
|
||||
switch_live_array_parse_json(json, 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, *conference_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))) {
|
||||
conference_desc = conference_cdr_json_render(conference, json);
|
||||
} else {
|
||||
conference_desc = cJSON_CreateObject();
|
||||
json_add_child_string(conference_desc, "conferenceDescription", "FreeSWITCH Conference");
|
||||
json_add_child_string(conference_desc, "conferenceState", "inactive");
|
||||
json_add_child_array(conference_desc, "users");
|
||||
json_add_child_array(conference_desc, "oldUsers");
|
||||
}
|
||||
} else {
|
||||
conference_desc = cJSON_CreateObject();
|
||||
json_add_child_string(conference_desc, "error", "Invalid action");
|
||||
}
|
||||
|
||||
json_add_child_string(conference_desc, "action", "conferenceDescription");
|
||||
|
||||
cJSON_AddItemToObject(reply, "data", conference_desc);
|
||||
|
||||
switch_safe_free(dup);
|
||||
|
||||
switch_event_channel_broadcast(event_channel, &reply, "mod_conference", conference_globals.event_channel_id);
|
||||
}
|
||||
|
||||
void conference_event_send_json(conference_obj_t *conference)
|
||||
{
|
||||
cJSON *event, *conference_desc = NULL;
|
||||
char *name = NULL, *domain = NULL, *dup_domain = NULL;
|
||||
char *event_channel = NULL;
|
||||
|
||||
if (!conference_utils_test_flag(conference, CFLAG_JSON_EVENTS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
conference_desc = conference_cdr_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", conference_desc);
|
||||
|
||||
switch_event_channel_broadcast(event_channel, &event, "mod_conference", conference_globals.event_channel_id);
|
||||
|
||||
switch_safe_free(dup_domain);
|
||||
switch_safe_free(event_channel);
|
||||
}
|
||||
|
||||
void conference_event_la_command_handler(switch_live_array_t *la, const char *cmd, const char *sessid, cJSON *jla, void *user_data)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void conference_event_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(conference_utils_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 (conference_utils_member_test_flag(member, MFLAG_SECOND_SCREEN)) {
|
||||
cJSON_AddItemToObject(data, "secondScreen", cJSON_CreateTrue());
|
||||
}
|
||||
|
||||
if (conference_utils_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, "conference_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", 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 conference_event_send_rfc(conference_obj_t *conference)
|
||||
{
|
||||
switch_event_t *event;
|
||||
char *body;
|
||||
char *name = NULL, *domain = NULL, *dup_domain = NULL;
|
||||
|
||||
if (!conference_utils_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_cdr_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_event_add_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;
|
||||
}
|
||||
|
||||
/* send a message to every member of the conference */
|
||||
void conference_event_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 && !conference_utils_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 conference_event_call_setup_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_utils_test_flag(conference, CFLAG_RFC4579)) {
|
||||
char *key = switch_mprintf("conference_%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)) {
|
||||
conference_send_notify(conference, "SIP/2.0 200 OK\r\n", call_id, SWITCH_TRUE);
|
||||
} else {
|
||||
conference_send_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 conference_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_utils_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_cdr_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 conference_event_pres_handler(switch_event_t *event)
|
||||
{
|
||||
char *to = switch_event_get_header(event, "to");
|
||||
char *domain_name = NULL;
|
||||
char *dup_to = NULL, *conference_name, *dup_conference_name = NULL;
|
||||
conference_obj_t *conference;
|
||||
|
||||
if (!to || strncasecmp(to, "conf+", 5) || !strchr(to, '@')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(dup_to = strdup(to))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
conference_name = dup_to + 5;
|
||||
|
||||
if ((domain_name = strchr(conference_name, '@'))) {
|
||||
*domain_name++ = '\0';
|
||||
}
|
||||
|
||||
dup_conference_name = switch_mprintf("%q@%q", conference_name, domain_name);
|
||||
|
||||
|
||||
if ((conference = conference_find(conference_name, NULL)) || (conference = conference_find(dup_conference_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", conference_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", conference_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", conference_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_conference_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) {
|
||||
conference_event_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;
|
||||
}
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
|
||||
*/
|
|
@ -0,0 +1,403 @@
|
|||
/*
|
||||
* 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_event_add_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_member_add_event_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) {
|
||||
conference_al_close(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_file_stop(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_file_play(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, ¶ms_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_utils_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_event_add_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 = conference_al_create(pool);
|
||||
if (conference_al_parse_position(fnode->al, position) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_core_file_close(&fnode->fh);
|
||||
switch_log_printf(SWITCH_CHANNEL_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)) {
|
||||
conference_video_fnode_check(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_file_local_play(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;
|
||||
}
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
|
||||
*/
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,431 @@
|
|||
/*
|
||||
* 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_record_launch_thread(conference_obj_t *conference, char *path, switch_bool_t autorec)
|
||||
{
|
||||
switch_thread_t *thread;
|
||||
switch_threadattr_t *thd_attr = NULL;
|
||||
switch_memory_pool_t *pool;
|
||||
conference_record_t *rec;
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
/* Create a node object */
|
||||
if (!(rec = switch_core_alloc(pool, sizeof(*rec)))) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Alloc Failure\n");
|
||||
switch_core_destroy_memory_pool(&pool);
|
||||
return;
|
||||
}
|
||||
|
||||
rec->conference = conference;
|
||||
rec->path = switch_core_strdup(pool, path);
|
||||
rec->pool = pool;
|
||||
rec->autorec = autorec;
|
||||
|
||||
switch_mutex_lock(conference->flag_mutex);
|
||||
rec->next = conference->rec_node_head;
|
||||
conference->rec_node_head = rec;
|
||||
switch_mutex_unlock(conference->flag_mutex);
|
||||
|
||||
switch_threadattr_create(&thd_attr, rec->pool);
|
||||
switch_threadattr_detach_set(thd_attr, 1);
|
||||
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
|
||||
switch_thread_create(&thread, thd_attr, conference_record_thread_run, rec, rec->pool);
|
||||
}
|
||||
|
||||
|
||||
/* 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 (conference_utils_member_test_flag(member, MFLAG_NOCHANNEL) && (!path || !strcmp(path, member->rec_path))) {
|
||||
if (!conference_utils_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);
|
||||
}
|
||||
|
||||
conference_utils_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 (conference_utils_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:
|
||||
conference_utils_member_clear_flag_locked(member, MFLAG_RUNNING);
|
||||
count++;
|
||||
break;
|
||||
case REC_ACTION_PAUSE:
|
||||
conference_utils_member_set_flag_locked(member, MFLAG_PAUSE_RECORDING);
|
||||
count = 1;
|
||||
break;
|
||||
case REC_ACTION_RESUME:
|
||||
conference_utils_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(conference_globals.hash_mutex);
|
||||
conference_globals.threads++;
|
||||
switch_mutex_unlock(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_utils_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_event_add_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_event_add_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_member_add(conference, member) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Joining Conference\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
while (conference_utils_member_test_flag(member, MFLAG_RUNNING) && conference_utils_test_flag(conference, CFLAG_RUNNING) && (conference->count + conference->count_ghosts)) {
|
||||
|
||||
len = 0;
|
||||
|
||||
mux_used = (uint32_t) switch_buffer_inuse(member->mux_buffer);
|
||||
|
||||
if (conference_utils_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;
|
||||
}
|
||||
conference_utils_member_clear_flag_locked(member, MFLAG_FLUSH_BUFFER);
|
||||
}
|
||||
|
||||
again:
|
||||
|
||||
if (switch_test_flag((&member->rec->fh), SWITCH_FILE_PAUSE)) {
|
||||
conference_utils_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 (!conference_utils_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");
|
||||
conference_utils_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_member_del(conference, member);
|
||||
|
||||
if (conference->canvas) {
|
||||
conference->canvas->send_keyframe = 1;
|
||||
}
|
||||
|
||||
switch_buffer_destroy(&member->audio_buffer);
|
||||
switch_buffer_destroy(&member->mux_buffer);
|
||||
conference_utils_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_event_add_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(conference_globals.hash_mutex);
|
||||
conference_globals.threads--;
|
||||
switch_mutex_unlock(conference_globals.hash_mutex);
|
||||
|
||||
switch_thread_rwlock_unlock(conference->rwlock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
|
||||
*/
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
const char *conference_utils_combine_flag_var(switch_core_session_t *session, const char *var_name)
|
||||
{
|
||||
switch_event_header_t *hp;
|
||||
switch_event_t *event, *cevent;
|
||||
char *ret = NULL;
|
||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||
|
||||
switch_core_get_variables(&event);
|
||||
switch_channel_get_variables(channel, &cevent);
|
||||
switch_event_merge(event, cevent);
|
||||
|
||||
|
||||
for (hp = event->headers; hp; hp = hp->next) {
|
||||
char *var = hp->name;
|
||||
char *val = hp->value;
|
||||
|
||||
if (!strcasecmp(var, var_name)) {
|
||||
if (hp->idx) {
|
||||
int i;
|
||||
for (i = 0; i < hp->idx; i++) {
|
||||
if (zstr(ret)) {
|
||||
ret = switch_core_session_sprintf(session, "%s", hp->array[i]);
|
||||
} else {
|
||||
ret = switch_core_session_sprintf(session, "%s|%s", ret, hp->array[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (zstr(ret)) {
|
||||
ret = switch_core_session_sprintf(session, "%s", val);
|
||||
} else {
|
||||
ret = switch_core_session_sprintf(session, "%s|%s", ret, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
switch_event_destroy(&event);
|
||||
switch_event_destroy(&cevent);
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void conference_utils_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 conference_utils_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 conference_utils_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 conference_utils_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_utils_set_flag(conference_obj_t *conference, conference_flag_t flag)
|
||||
{
|
||||
conference->flags[flag] = 1;
|
||||
}
|
||||
void conference_utils_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_utils_clear_flag(conference_obj_t *conference, conference_flag_t flag)
|
||||
{
|
||||
conference->flags[flag] = 0;
|
||||
}
|
||||
void conference_utils_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_utils_test_flag(conference_obj_t *conference, conference_flag_t flag)
|
||||
{
|
||||
return !!conference->flags[flag];
|
||||
}
|
||||
|
||||
#if 0
|
||||
void conference_utils_conference_utils_set_mflag(conference_obj_t *conference, member_flag_t mflag)
|
||||
{
|
||||
conference->mflags[mflag] = 1;
|
||||
}
|
||||
|
||||
void conference_utils_clear_mflag(conference_obj_t *conference, member_flag_t mflag)
|
||||
{
|
||||
conference->mflags[mflag] = 0;
|
||||
}
|
||||
|
||||
switch_bool_t conference_utils_test_mflag(conference_obj_t *conference, member_flag_t mflag)
|
||||
{
|
||||
return !!conference->mflags[mflag];
|
||||
}
|
||||
#endif
|
||||
|
||||
void conference_utils_member_set_flag(conference_member_t *member, member_flag_t flag)
|
||||
{
|
||||
member->flags[flag] = 1;
|
||||
}
|
||||
|
||||
void conference_utils_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 conference_utils_member_clear_flag(conference_member_t *member, member_flag_t flag)
|
||||
{
|
||||
member->flags[flag] = 0;
|
||||
}
|
||||
void conference_utils_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 conference_utils_member_test_flag(conference_member_t *member, member_flag_t flag)
|
||||
{
|
||||
return !!member->flags[flag];
|
||||
}
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
|
||||
*/
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue