1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * Anthony Minessale II <anthm@freeswitch.org>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Anthony Minessale II <anthm@freeswitch.org>
27 * Neal Horman <neal at wanlink dot com>
28 * Bret McDanel <trixter at 0xdecafbad dot com>
29 * Dale Thatcher <freeswitch at dalethatcher dot com>
30 * Chris Danielson <chris at maxpowersoft dot com>
31 * Rupa Schomaker <rupa@rupa.com>
32 * David Weekly <david@weekly.org>
33 * Joao Mesquita <jmesquita@gmail.com>
34 * Raymond Chandler <intralanman@freeswitch.org>
35 * Seven Du <dujinfang@gmail.com>
36 * Emmanuel Schmidbauer <e.schmidbauer@gmail.com>
37 * William King <william.king@quentustech.com>
38 *
39 * mod_conference.c -- Software Conference Bridge
40 *
41 */
42 #include <mod_conference.h>
43
44 SWITCH_MODULE_LOAD_FUNCTION(mod_conference_load);
45 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_conference_shutdown);
46 SWITCH_MODULE_DEFINITION(mod_conference, mod_conference_load, mod_conference_shutdown, NULL);
47 SWITCH_STANDARD_APP(conference_function);
48
49 const char *mod_conference_app_name = "conference";
50 char *mod_conference_cf_name = "conference.conf";
51 conference_globals_t conference_globals = {0};
52 int EC = 0;
53 char *api_syntax = NULL;
54
SWITCH_STANDARD_API(conference_api_main)55 SWITCH_STANDARD_API(conference_api_main){
56 return conference_api_main_real(cmd, session, stream);
57 }
58
59 /* Return a Distinct ID # */
next_member_id(void)60 uint32_t next_member_id(void)
61 {
62 uint32_t id;
63
64 switch_mutex_lock(conference_globals.id_mutex);
65 id = ++conference_globals.id_pool;
66 switch_mutex_unlock(conference_globals.id_mutex);
67
68 return id;
69 }
70
conference_list(conference_obj_t * conference,switch_stream_handle_t * stream,char * delim)71 void conference_list(conference_obj_t *conference, switch_stream_handle_t *stream, char *delim)
72 {
73 conference_member_t *member = NULL;
74
75 switch_assert(conference != NULL);
76 switch_assert(stream != NULL);
77 switch_assert(delim != NULL);
78
79 switch_mutex_lock(conference->member_mutex);
80
81 for (member = conference->members; member; member = member->next) {
82 switch_channel_t *channel;
83 switch_caller_profile_t *profile;
84 char *uuid;
85 char *name;
86 uint32_t count = 0;
87 switch_bool_t hold = conference_utils_member_test_flag(member, MFLAG_HOLD);
88
89 if (conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
90 continue;
91 }
92
93 uuid = switch_core_session_get_uuid(member->session);
94 channel = switch_core_session_get_channel(member->session);
95 profile = switch_channel_get_caller_profile(channel);
96 name = switch_channel_get_name(channel);
97
98 stream->write_function(stream, "%u%s%s%s%s%s%s%s%s%s",
99 member->id, delim, name, delim, uuid, delim, profile->caller_id_name, delim, profile->caller_id_number, delim);
100
101 if (!hold && conference_utils_member_test_flag(member, MFLAG_CAN_HEAR)) {
102 stream->write_function(stream, "hear");
103 count++;
104 }
105
106 if (!hold && conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)) {
107 stream->write_function(stream, "%s%s", count ? "|" : "", "speak");
108 count++;
109 }
110
111 if (!hold && conference_utils_member_test_flag(member, MFLAG_TALKING)) {
112 stream->write_function(stream, "%s%s", count ? "|" : "", "talking");
113 count++;
114 }
115
116 if (hold) {
117 stream->write_function(stream, "%s%s", count ? "|" : "", "hold");
118 count++;
119 }
120
121 if (switch_channel_test_flag(switch_core_session_get_channel(member->session), CF_VIDEO)) {
122 stream->write_function(stream, "%s%s", count ? "|" : "", "video");
123 count++;
124 }
125
126 if (member->id == member->conference->floor_holder) {
127 stream->write_function(stream, "%s%s", count ? "|" : "", "floor");
128 count++;
129 }
130
131 if (member->id == member->conference->video_floor_holder) {
132 stream->write_function(stream, "%s%s", count ? "|" : "", "vid-floor");
133 count++;
134 }
135
136 if (conference_utils_member_test_flag(member, MFLAG_MOD)) {
137 stream->write_function(stream, "%s%s", count ? "|" : "", "moderator");
138 count++;
139 }
140
141 if (conference_utils_member_test_flag(member, MFLAG_GHOST)) {
142 stream->write_function(stream, "%s%s", count ? "|" : "", "ghost");
143 count++;
144 }
145
146 if (member->video_reservation_id) {
147 stream->write_function(stream, "%s%s%s", count ? "|" : "", "res-id:", member->video_reservation_id);
148 count++;
149 }
150
151 if (member->video_role_id) {
152 stream->write_function(stream, "%s%s%s", count ? "|" : "", "role-id:", member->video_role_id);
153 count++;
154 }
155
156 stream->write_function(stream, "%s%d%s%d%s%d\n", delim,
157 member->volume_in_level,
158 delim, member->volume_out_level, delim, member->energy_level);
159 }
160
161 switch_mutex_unlock(conference->member_mutex);
162 }
163
conference_send_notify(conference_obj_t * conference,const char * status,const char * call_id,switch_bool_t final)164 void conference_send_notify(conference_obj_t *conference, const char *status, const char *call_id, switch_bool_t final)
165 {
166 switch_event_t *event;
167 char *name = NULL, *domain = NULL, *dup_domain = NULL;
168
169 if (!conference_utils_test_flag(conference, CFLAG_RFC4579)) {
170 return;
171 }
172
173 if (!(name = conference->name)) {
174 name = "conference";
175 }
176
177 if (!(domain = conference->domain)) {
178 dup_domain = switch_core_get_domain(SWITCH_TRUE);
179 if (!(domain = dup_domain)) {
180 domain = "cluecon.com";
181 }
182 }
183
184
185 if (switch_event_create(&event, SWITCH_EVENT_CONFERENCE_DATA) == SWITCH_STATUS_SUCCESS) {
186 event->flags |= EF_UNIQ_HEADERS;
187
188 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "conference-name", name);
189 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "conference-domain", domain);
190 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "conference-event", "refer");
191 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call_id", call_id);
192
193 if (final) {
194 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "final", "true");
195 }
196
197
198 switch_event_add_body(event, "%s", status);
199 switch_event_fire(&event);
200 }
201
202 switch_safe_free(dup_domain);
203
204 }
205
206
207 /* Main monitor thread (1 per distinct conference room) */
conference_thread_run(switch_thread_t * thread,void * obj)208 void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, void *obj)
209 {
210 conference_obj_t *conference = (conference_obj_t *) obj;
211 conference_member_t *imember, *omember;
212 uint32_t samples = switch_samples_per_packet(conference->rate, conference->interval);
213 uint32_t bytes = samples * 2 * conference->channels;
214 uint8_t ready = 0, total = 0;
215 switch_timer_t timer = { 0 };
216 switch_event_t *event;
217 uint8_t *file_frame;
218 uint8_t *async_file_frame;
219 int16_t *bptr;
220 uint32_t x = 0;
221 int32_t z = 0;
222 conference_cdr_node_t *np;
223
224 file_frame = switch_core_alloc(conference->pool, SWITCH_RECOMMENDED_BUFFER_SIZE);
225 async_file_frame = switch_core_alloc(conference->pool, SWITCH_RECOMMENDED_BUFFER_SIZE);
226
227 if (switch_core_timer_init(&timer, conference->timer_name, conference->interval, samples, conference->pool) == SWITCH_STATUS_SUCCESS) {
228 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Setup timer success interval: %u samples: %u\n", conference->interval, samples);
229 } else {
230 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Timer Setup Failed. Conference Cannot Start\n");
231 return NULL;
232 }
233
234 switch_mutex_lock(conference_globals.hash_mutex);
235 conference_globals.threads++;
236 switch_mutex_unlock(conference_globals.hash_mutex);
237
238 conference->auto_recording = 0;
239 conference->record_count = 0;
240
241 while (conference_globals.running && !conference_utils_test_flag(conference, CFLAG_DESTRUCT)) {
242 switch_size_t file_sample_len = samples;
243 switch_size_t file_data_len = samples * 2 * conference->channels;
244 int has_file_data = 0, members_with_video = 0, members_with_avatar = 0, members_seeing_video = 0;
245 int nomoh = 0;
246 uint32_t floor_holder;
247 switch_status_t moh_status = SWITCH_STATUS_SUCCESS;
248
249 /* Sync the conference to a single timing source */
250 if (switch_core_timer_next(&timer) != SWITCH_STATUS_SUCCESS) {
251 conference_utils_set_flag(conference, CFLAG_DESTRUCT);
252 break;
253 }
254
255 switch_mutex_lock(conference->mutex);
256 has_file_data = ready = total = 0;
257
258 floor_holder = conference->floor_holder;
259
260 for (imember = conference->members; imember; imember = imember->next) {
261 if (!zstr(imember->text_framedata)) {
262 switch_frame_t frame = { 0 };
263 char *framedata;
264 uint32_t framedatalen;
265 const char *caller_id_name = switch_channel_get_variable(imember->channel, "caller_id_name");
266 unsigned char CR[3] = TEXT_UNICODE_LINEFEED;
267
268
269 switch_mutex_lock(imember->text_mutex);
270
271 framedatalen = strlen(imember->text_framedata) + strlen(caller_id_name) + 6;
272
273 switch_zmalloc(framedata, framedatalen);
274
275 switch_snprintf(framedata, framedatalen, "%s::\n%s", caller_id_name, imember->text_framedata);
276 memcpy(framedata + strlen(framedata), CR, sizeof(CR));
277
278
279 frame.data = framedata;
280 frame.datalen = framedatalen;
281
282 for (omember = conference->members; omember; omember = omember->next) {
283 if (omember != imember && omember->session) {
284 switch_core_session_write_text_frame(omember->session, &frame, 0, 0);
285 }
286 }
287
288 free(framedata);
289
290 imember->text_framedata[0] = '\0';
291
292 switch_mutex_unlock(imember->text_mutex);
293 }
294 }
295
296 /* Read one frame of audio from each member channel and save it for redistribution */
297 for (imember = conference->members; imember; imember = imember->next) {
298 uint32_t buf_read = 0;
299 total++;
300 imember->read = 0;
301
302 if (conference_utils_member_test_flag(imember, MFLAG_RUNNING) && imember->session) {
303 switch_channel_t *channel = switch_core_session_get_channel(imember->session);
304 switch_media_flow_t video_media_flow;
305
306 if ((!floor_holder || (imember->id != conference->floor_holder && imember->score_iir > SCORE_IIR_SPEAKING_MAX && (conference->floor_holder_score_iir < SCORE_IIR_SPEAKING_MIN)))) {// &&
307 //(!conference_utils_test_flag(conference, CFLAG_VID_FLOOR) || switch_channel_test_flag(channel, CF_VIDEO))) {
308
309
310 if (!conference_utils_member_test_flag(imember, MFLAG_DED_VID_LAYER) || conference_utils_test_flag(conference, CFLAG_DED_VID_LAYER_AUDIO_FLOOR)) {
311 conference_member_set_floor_holder(conference, imember, 0);
312 floor_holder = conference->floor_holder;
313 }
314 }
315
316 video_media_flow = switch_core_session_media_flow(imember->session, SWITCH_MEDIA_TYPE_VIDEO);
317
318 if (video_media_flow != imember->video_media_flow) {
319 imember->video_media_flow = video_media_flow;
320
321 if (imember->video_media_flow == SWITCH_MEDIA_FLOW_SENDONLY) {
322 conference_utils_member_clear_flag(imember, MFLAG_CAN_BE_SEEN);
323 conference_video_find_floor(imember, SWITCH_FALSE);
324 } else {
325 conference_utils_member_set_flag(imember, MFLAG_CAN_BE_SEEN);
326 conference_video_find_floor(imember, SWITCH_TRUE);
327 switch_core_session_request_video_refresh(imember->session);
328 }
329 }
330
331 if (switch_channel_ready(channel) &&
332 switch_channel_test_flag(channel, CF_VIDEO_READY) &&
333 imember->video_media_flow != SWITCH_MEDIA_FLOW_SENDONLY &&
334 !conference_utils_member_test_flag(imember, MFLAG_SECOND_SCREEN) &&
335 !conference_utils_member_test_flag(imember, MFLAG_HOLD) &&
336 (!conference_utils_test_flag(conference, CFLAG_VIDEO_MUTE_EXIT_CANVAS) ||
337 conference_utils_member_test_flag(imember, MFLAG_CAN_BE_SEEN))) {
338 members_with_video++;
339 }
340
341 if (switch_channel_ready(channel) &&
342 switch_channel_test_flag(channel, CF_VIDEO_READY) &&
343 imember->video_media_flow != SWITCH_MEDIA_FLOW_SENDONLY &&
344 !conference_utils_member_test_flag(imember, MFLAG_SECOND_SCREEN)) {
345 members_seeing_video++;
346 }
347
348 if (!conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS)) {
349 if (imember->avatar_png_img && !switch_channel_test_flag(channel, CF_VIDEO)) {
350 members_with_avatar++;
351 }
352 } else {
353 members_with_avatar = 0;
354 }
355
356 if (conference_utils_member_test_flag(imember, MFLAG_NOMOH)) {
357 nomoh++;
358 }
359 }
360
361 conference_utils_member_clear_flag_locked(imember, MFLAG_HAS_AUDIO);
362 switch_mutex_lock(imember->audio_in_mutex);
363
364 if (switch_buffer_inuse(imember->audio_buffer) >= bytes
365 && (buf_read = (uint32_t) switch_buffer_read(imember->audio_buffer, imember->frame, bytes))) {
366 imember->read = buf_read;
367 conference_utils_member_set_flag_locked(imember, MFLAG_HAS_AUDIO);
368 ready++;
369 }
370 switch_mutex_unlock(imember->audio_in_mutex);
371 }
372
373 conference->members_with_video = members_with_video;
374 conference->members_seeing_video = members_seeing_video;
375 conference->members_with_avatar = members_with_avatar;
376
377 if (floor_holder != conference->floor_holder) {
378 conference_member_set_floor_holder(conference, NULL, floor_holder);
379 }
380
381 if (conference_utils_test_flag(conference, CFLAG_NO_MOH)) {
382 nomoh++;
383 }
384
385 if (conference->moh_wait > 0) {
386 conference->moh_wait--;
387 } else {
388 char *moh_sound = conference->tmp_moh_sound;
389
390 if (!moh_sound) {
391 moh_sound = conference->moh_sound;
392 }
393
394 if (conference->perpetual_sound && !conference->async_fnode) {
395 moh_status = conference_file_play(conference, conference->perpetual_sound, CONF_DEFAULT_LEADIN, NULL, 1);
396 } else if (moh_sound && ((nomoh == 0 && conference->count == 1)
397 || conference_utils_test_flag(conference, CFLAG_WAIT_MOD)) &&
398 !conference->async_fnode && !conference->fnode) {
399 moh_status = conference_file_play(conference, moh_sound, CONF_DEFAULT_LEADIN, NULL, 1);
400 }
401 }
402
403 if (!conference->moh_wait && moh_status != SWITCH_STATUS_SUCCESS) {
404 conference->moh_wait = 2000 / conference->interval;
405 }
406
407 /* Find if no one talked for more than x number of second */
408 if (conference->terminate_on_silence && conference->count > 1) {
409 int is_talking = 0;
410
411 for (imember = conference->members; imember; imember = imember->next) {
412 if (switch_epoch_time_now(NULL) - imember->join_time <= conference->terminate_on_silence) {
413 is_talking++;
414 } else if (imember->last_talking != 0 && switch_epoch_time_now(NULL) - imember->last_talking <= conference->terminate_on_silence) {
415 is_talking++;
416 }
417 }
418 if (is_talking == 0) {
419 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Conference has been idle for over %d seconds, terminating\n", conference->terminate_on_silence);
420 conference_utils_set_flag(conference, CFLAG_DESTRUCT);
421 }
422 }
423
424 /* Start auto recording if there's the minimum number of required participants. */
425 if (conference->auto_record && !conference->auto_recording && (conference->count >= conference->min_recording_participants)) {
426 conference->auto_recording++;
427 conference->record_count++;
428 imember = conference->members;
429 if (imember) {
430 switch_channel_t *channel = switch_core_session_get_channel(imember->session);
431 char *rfile = switch_channel_expand_variables(channel, conference->auto_record);
432
433 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Auto recording file: %s\n", rfile);
434 conference_record_launch_thread(conference, rfile, -1, SWITCH_TRUE);
435
436 if (rfile != conference->auto_record) {
437 conference->record_filename = switch_core_strdup(conference->pool, rfile);
438 switch_safe_free(rfile);
439 } else {
440 conference->record_filename = switch_core_strdup(conference->pool, conference->auto_record);
441 }
442
443 /* Set the conference recording variable for each member */
444 for (omember = conference->members; omember; omember = omember->next) {
445 if (!omember->session) continue;
446 channel = switch_core_session_get_channel(omember->session);
447 switch_channel_set_variable(channel, "conference_recording", conference->record_filename);
448 switch_channel_set_variable_printf(channel, "conference_recording_canvas", "%d", conference->auto_record_canvas + 1);
449 }
450 } else {
451 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Auto Record Failed. No members in conference.\n");
452 }
453 }
454
455 switch_mutex_lock(conference->file_mutex);
456 /* If a file or speech event is being played */
457 if (conference->fnode && !switch_test_flag(conference->fnode, NFLAG_PAUSE)) {
458 /* Lead in time */
459 if (conference->fnode->leadin) {
460 conference->fnode->leadin--;
461 } else if (!conference->fnode->done) {
462 file_sample_len = samples;
463
464 if (conference->fnode->type == NODE_TYPE_SPEECH) {
465 switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_BLOCKING;
466 switch_size_t speech_len = file_data_len;
467
468 if (conference->fnode->al) {
469 speech_len /= 2;
470 }
471
472 if (switch_core_speech_read_tts(conference->fnode->sh, file_frame, &speech_len, &flags) == SWITCH_STATUS_SUCCESS) {
473
474 if (conference->fnode->al) {
475 conference_al_process(conference->fnode->al, file_frame, speech_len, conference->rate);
476 }
477
478 file_sample_len = file_data_len / 2 / conference->fnode->sh->channels;
479
480
481 } else {
482 file_sample_len = file_data_len = 0;
483 }
484 } else if (conference->fnode->type == NODE_TYPE_FILE) {
485 switch_core_file_read(&conference->fnode->fh, file_frame, &file_sample_len);
486
487 if (conference->fnode->fh.vol) {
488 switch_change_sln_volume_granular((void *)file_frame, (uint32_t)file_sample_len * conference->fnode->fh.channels,
489 conference->fnode->fh.vol);
490 }
491 if (conference->fnode->al) {
492 conference_al_process(conference->fnode->al, file_frame, file_sample_len * 2, conference->fnode->fh.samplerate);
493 }
494 }
495
496 if (file_sample_len <= 0) {
497 if (conference->fnode->loops) {
498 if (--conference->fnode->loops < 0) {
499 conference->fnode->loops = -1;
500 }
501
502 if (conference->fnode->loops) {
503 uint32_t pos = 0;
504 switch_core_file_seek(&conference->fnode->fh, &pos, 0, SEEK_SET);
505 }
506 }
507
508 if (!conference->fnode->loops) {
509 conference->fnode->done++;
510 }
511 } else {
512 has_file_data = 1;
513 }
514 }
515 }
516
517 if (conference->async_fnode) {
518 /* Lead in time */
519 if (conference->async_fnode->leadin) {
520 conference->async_fnode->leadin--;
521 } else if (!conference->async_fnode->done) {
522 file_sample_len = samples;
523 switch_core_file_read(&conference->async_fnode->fh, async_file_frame, &file_sample_len);
524 if (conference->async_fnode->al) {
525 conference_al_process(conference->async_fnode->al, file_frame, file_sample_len * 2, conference->async_fnode->fh.samplerate);
526 }
527 if (file_sample_len <= 0) {
528 if (conference->async_fnode->loops) {
529 if (--conference->async_fnode->loops < 0) {
530 conference->async_fnode->loops = -1;
531 }
532
533 if (conference->async_fnode->loops) {
534 uint32_t pos = 0;
535 switch_core_file_seek(&conference->async_fnode->fh, &pos, 0, SEEK_SET);
536 }
537 }
538
539 if (!conference->async_fnode->loops) {
540 conference->async_fnode->done++;
541 }
542 } else {
543 if (has_file_data) {
544 switch_size_t x;
545 for (x = 0; x < file_sample_len * conference->channels; x++) {
546 int32_t z;
547 int16_t *muxed;
548
549 muxed = (int16_t *) file_frame;
550 bptr = (int16_t *) async_file_frame;
551 z = muxed[x] + bptr[x];
552 switch_normalize_to_16bit(z);
553 muxed[x] = (int16_t) z;
554 }
555 } else {
556 memcpy(file_frame, async_file_frame, file_sample_len * 2 * conference->channels);
557 has_file_data = 1;
558 }
559 }
560 }
561 }
562 switch_mutex_unlock(conference->file_mutex);
563
564 if (ready || has_file_data) {
565 /* Use more bits in the main_frame to preserve the exact sum of the audio samples. */
566 int main_frame[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 };
567 int16_t write_frame[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 };
568
569
570 /* Init the main frame with file data if there is any. */
571 bptr = (int16_t *) file_frame;
572 if (has_file_data && file_sample_len) {
573
574 for (x = 0; x < bytes / 2; x++) {
575 if (x <= file_sample_len * conference->channels) {
576 main_frame[x] = (int32_t) bptr[x];
577 } else {
578 memset(&main_frame[x], 255, sizeof(main_frame[x]));
579 }
580 }
581 }
582
583 conference->mux_loop_count = 0;
584 conference->member_loop_count = 0;
585
586
587 /* Copy audio from every member known to be producing audio into the main frame. */
588 for (omember = conference->members; omember; omember = omember->next) {
589 conference->member_loop_count++;
590
591 if (!(conference_utils_member_test_flag(omember, MFLAG_RUNNING) && conference_utils_member_test_flag(omember, MFLAG_HAS_AUDIO))) {
592 continue;
593 }
594
595 bptr = (int16_t *) omember->frame;
596 for (x = 0; x < omember->read / 2; x++) {
597 main_frame[x] += (int32_t) bptr[x];
598 }
599 }
600
601 /* Create write frame once per member who is not deaf for each sample in the main frame
602 check if our audio is involved and if so, subtract it from the sample so we don't hear ourselves.
603 Since main frame was 32 bit int, we did not lose any detail, now that we have to convert to 16 bit we can
604 cut it off at the min and max range if need be and write the frame to the output buffer.
605 */
606 for (omember = conference->members; omember; omember = omember->next) {
607 switch_size_t ok = 1;
608
609 if (!conference_utils_member_test_flag(omember, MFLAG_RUNNING) ||
610 (!conference_utils_member_test_flag(omember, MFLAG_NOCHANNEL) && !switch_channel_test_flag(omember->channel, CF_AUDIO))) {
611 continue;
612 }
613
614 if (!conference_utils_member_test_flag(omember, MFLAG_CAN_HEAR)) {
615 switch_mutex_lock(omember->audio_out_mutex);
616 memset(write_frame, 255, bytes);
617 ok = switch_buffer_write(omember->mux_buffer, write_frame, bytes);
618 switch_mutex_unlock(omember->audio_out_mutex);
619 if (!ok) {
620 switch_mutex_unlock(conference->mutex);
621 goto end;
622 }
623 continue;
624 }
625
626 bptr = (int16_t *) omember->frame;
627
628 for (x = 0; x < bytes / 2 ; x++) {
629 z = main_frame[x];
630
631 /* bptr[x] represents my own contribution to this audio sample */
632 if (conference_utils_member_test_flag(omember, MFLAG_HAS_AUDIO) && x <= omember->read / 2) {
633 z -= (int32_t) bptr[x];
634 }
635
636 /* when there are relationships, we have to do more work by scouring all the members to see if there are any
637 reasons why we should not be hearing a paticular member, and if not, delete their samples as well.
638 */
639 if (conference->relationship_total) {
640 for (imember = conference->members; imember; imember = imember->next) {
641 if (imember != omember && conference_utils_member_test_flag(imember, MFLAG_HAS_AUDIO)) {
642 conference_relationship_t *rel;
643 switch_size_t found = 0;
644 int16_t *rptr = (int16_t *) imember->frame;
645 for (rel = imember->relationships; rel; rel = rel->next) {
646 if ((rel->id == omember->id || rel->id == 0) && !switch_test_flag(rel, RFLAG_CAN_SPEAK)) {
647 z -= (int32_t) rptr[x];
648 found = 1;
649 break;
650 }
651 }
652 if (!found) {
653 for (rel = omember->relationships; rel; rel = rel->next) {
654 if ((rel->id == imember->id || rel->id == 0) && !switch_test_flag(rel, RFLAG_CAN_HEAR)) {
655 z -= (int32_t) rptr[x];
656 break;
657 }
658 }
659 }
660
661 }
662 }
663 }
664
665 /* Now we can convert to 16 bit. */
666 switch_normalize_to_16bit(z);
667 write_frame[x] = (int16_t) z;
668 }
669
670 if (!omember->channel || switch_channel_test_flag(omember->channel, CF_AUDIO)) {
671 switch_mutex_lock(omember->audio_out_mutex);
672 ok = switch_buffer_write(omember->mux_buffer, write_frame, bytes);
673 switch_mutex_unlock(omember->audio_out_mutex);
674 if (!ok) {
675 switch_mutex_unlock(conference->mutex);
676 goto end;
677 }
678 }
679 }
680 } else { /* There is no source audio. Push silence into all of the buffers */
681 int16_t write_frame[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 };
682
683 if (conference->comfort_noise_level) {
684 switch_generate_sln_silence(write_frame, samples, conference->channels, conference->comfort_noise_level * (conference->rate / 8000));
685 } else {
686 memset(write_frame, 255, bytes);
687 }
688
689 for (omember = conference->members; omember; omember = omember->next) {
690 switch_size_t ok = 1;
691
692 if (!conference_utils_member_test_flag(omember, MFLAG_RUNNING) ||
693 (!conference_utils_member_test_flag(omember, MFLAG_NOCHANNEL) && !switch_channel_test_flag(omember->channel, CF_AUDIO))) {
694 continue;
695 }
696
697 switch_mutex_lock(omember->audio_out_mutex);
698 ok = switch_buffer_write(omember->mux_buffer, write_frame, bytes);
699 switch_mutex_unlock(omember->audio_out_mutex);
700
701 if (!ok) {
702 switch_mutex_unlock(conference->mutex);
703 goto end;
704 }
705 }
706 }
707
708 if (conference->async_fnode && conference->async_fnode->done) {
709 switch_memory_pool_t *pool;
710
711 if (conference->canvases[0] && conference->async_fnode->layer_id > -1 ) {
712 conference_video_canvas_del_fnode_layer(conference, conference->async_fnode);
713 }
714
715 switch_mutex_lock(conference->file_mutex);
716 conference_file_close(conference, conference->async_fnode);
717 pool = conference->async_fnode->pool;
718 conference->async_fnode = NULL;
719 switch_core_destroy_memory_pool(&pool);
720 switch_mutex_unlock(conference->file_mutex);
721 }
722
723 if (conference->fnode && conference->fnode->done) {
724 conference_file_node_t *fnode;
725 switch_memory_pool_t *pool;
726
727 switch_mutex_lock(conference->file_mutex);
728 if (conference->fnode->type != NODE_TYPE_SPEECH) {
729 conference_file_close(conference, conference->fnode);
730 }
731
732 if (conference->canvases[0] && conference->fnode->layer_id > -1 ) {
733 conference_video_canvas_del_fnode_layer(conference, conference->fnode);
734 }
735
736
737 fnode = conference->fnode;
738 conference->fnode = conference->fnode->next;
739
740 if (conference->fnode) {
741 conference_video_fnode_check(conference->fnode, -1);
742 }
743
744
745 pool = fnode->pool;
746 fnode = NULL;
747 switch_core_destroy_memory_pool(&pool);
748 switch_mutex_unlock(conference->file_mutex);
749 }
750
751 if (!conference->end_count && conference->endconference_time &&
752 switch_epoch_time_now(NULL) - conference->endconference_time > conference->endconference_grace_time) {
753 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Conference %s: endconf grace time exceeded (%u)\n",
754 conference->name, conference->endconference_grace_time);
755 conference_utils_set_flag(conference, CFLAG_DESTRUCT);
756 conference_utils_set_flag(conference, CFLAG_ENDCONF_FORCED);
757 }
758
759 switch_mutex_unlock(conference->mutex);
760 }
761 /* Rinse ... Repeat */
762 end:
763
764 if (conference_utils_test_flag(conference, CFLAG_OUTCALL)) {
765 conference->cancel_cause = SWITCH_CAUSE_ORIGINATOR_CANCEL;
766 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Ending pending outcall channels for Conference: '%s'\n", conference->name);
767 while(conference->originating) {
768 switch_yield(200000);
769 }
770 }
771
772 conference_send_presence(conference);
773
774 switch_mutex_lock(conference->mutex);
775 conference_file_stop(conference, FILE_STOP_ASYNC);
776 conference_file_stop(conference, FILE_STOP_ALL);
777
778 switch_mutex_lock(conference->member_mutex);
779 for (imember = conference->members; imember; imember = imember->next) {
780 switch_channel_t *channel;
781
782 if (!conference_utils_member_test_flag(imember, MFLAG_NOCHANNEL)) {
783 channel = switch_core_session_get_channel(imember->session);
784
785 if (!switch_false(switch_channel_get_variable(channel, "hangup_after_conference"))) {
786 /* add this little bit to preserve the bridge cause code in case of an early media call that */
787 /* never answers */
788 if (conference_utils_test_flag(conference, CFLAG_ANSWERED)) {
789 switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
790 } else {
791 /* put actual cause code from outbound channel hangup here */
792 switch_channel_hangup(channel, conference->bridge_hangup_cause);
793 }
794 }
795 }
796
797 conference_utils_member_clear_flag_locked(imember, MFLAG_RUNNING);
798 }
799 switch_mutex_unlock(conference->member_mutex);
800 switch_mutex_unlock(conference->mutex);
801
802 if (conference->vh[0].up == 1) {
803 conference->vh[0].up = -1;
804 }
805
806 if (conference->vh[1].up == 1) {
807 conference->vh[1].up = -1;
808 }
809
810 while (conference->vh[0].up || conference->vh[1].up) {
811 switch_cond_next();
812 }
813
814 switch_core_timer_destroy(&timer);
815 switch_mutex_lock(conference_globals.hash_mutex);
816 if (conference_utils_test_flag(conference, CFLAG_INHASH)) {
817 switch_core_hash_delete(conference_globals.conference_hash, conference->name);
818 }
819 switch_mutex_unlock(conference_globals.hash_mutex);
820
821 conference_utils_clear_flag(conference, CFLAG_VIDEO_MUXING);
822
823 for (x = 0; x <= conference->canvas_count; x++) {
824 if (conference->canvases[x] && conference->canvases[x]->video_muxing_thread) {
825 switch_status_t st = 0;
826 switch_thread_join(&st, conference->canvases[x]->video_muxing_thread);
827 conference->canvases[x]->video_muxing_thread = NULL;
828 }
829 }
830
831 conference_close_open_files(conference);
832
833 /* Wait till everybody is out */
834 conference_utils_clear_flag_locked(conference, CFLAG_RUNNING);
835 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write Lock ON\n");
836 switch_thread_rwlock_wrlock(conference->rwlock);
837 switch_thread_rwlock_unlock(conference->rwlock);
838 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Write Lock OFF\n");
839
840 if (conference->la) {
841 switch_live_array_destroy(&conference->la);
842 }
843
844 if (conference->sh) {
845 switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
846 switch_core_speech_close(&conference->lsh, &flags);
847 conference->sh = NULL;
848 }
849
850 conference->end_time = switch_epoch_time_now(NULL);
851
852 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT);
853 conference_event_add_data(conference, event);
854 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "conference-destroy");
855 switch_event_fire(&event);
856
857 switch_mutex_lock(conference->member_mutex);
858 conference_cdr_render(conference);
859
860 for (np = conference->cdr_nodes; np; np = np->next) {
861 if (np->var_event) {
862 switch_event_destroy(&np->var_event);
863 }
864 }
865 switch_mutex_unlock(conference->member_mutex);
866
867 switch_mutex_lock(conference_globals.setup_mutex);
868 if (conference->layout_hash) {
869 switch_core_hash_destroy(&conference->layout_hash);
870 }
871 switch_mutex_unlock(conference_globals.setup_mutex);
872
873 if (conference->layout_group_hash) {
874 switch_core_hash_destroy(&conference->layout_group_hash);
875 }
876
877 switch_mutex_lock(conference->flag_mutex);
878 switch_event_destroy(&conference->variables);
879 switch_mutex_unlock(conference->flag_mutex);
880
881 if (conference->pool) {
882 switch_memory_pool_t *pool = conference->pool;
883 switch_core_destroy_memory_pool(&pool);
884 }
885
886 switch_mutex_lock(conference_globals.hash_mutex);
887 conference_globals.threads--;
888 switch_mutex_unlock(conference_globals.hash_mutex);
889
890 return NULL;
891 }
892
893
894
895 /* Say some thing with TTS in the conference room */
conference_say(conference_obj_t * conference,const char * text,uint32_t leadin)896 switch_status_t conference_say(conference_obj_t *conference, const char *text, uint32_t leadin)
897 {
898 switch_status_t status = SWITCH_STATUS_FALSE;
899 conference_file_node_t *fnode, *nptr;
900 switch_memory_pool_t *pool;
901 switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
902 uint32_t count;
903 switch_event_t *params = NULL;
904 char *fp = NULL;
905 int channels;
906 const char *position = NULL;
907 const char *tts_engine = NULL, *tts_voice = NULL;
908
909 switch_assert(conference != NULL);
910
911 channels = conference->channels;
912
913 if (zstr(text)) {
914 return SWITCH_STATUS_GENERR;
915 }
916
917
918 switch_mutex_lock(conference->mutex);
919 switch_mutex_lock(conference->member_mutex);
920 count = conference->count;
921 switch_mutex_unlock(conference->member_mutex);
922 switch_mutex_unlock(conference->mutex);
923
924 if (!count) {
925 return SWITCH_STATUS_FALSE;
926 }
927
928 /* Setup a memory pool to use. */
929 if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
930 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Pool Failure\n");
931 return SWITCH_STATUS_MEMERR;
932 }
933
934 /* Create a node object */
935 if (!(fnode = switch_core_alloc(pool, sizeof(*fnode)))) {
936 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Alloc Failure\n");
937 switch_core_destroy_memory_pool(&pool);
938 return SWITCH_STATUS_MEMERR;
939 }
940
941 fnode->conference = conference;
942 fnode->layer_id = -1;
943
944 if (*text == '{') {
945 char *new_fp;
946
947 fp = switch_core_strdup(pool, text);
948 switch_assert(fp);
949
950 if (switch_event_create_brackets(fp, '{', '}', ',', ¶ms, &new_fp, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) {
951 new_fp = fp;
952 }
953
954 text = new_fp;
955 }
956
957
958 fnode->type = NODE_TYPE_SPEECH;
959 fnode->leadin = leadin;
960
961 if (params) {
962 tts_engine = switch_event_get_header(params, "tts_engine");
963 tts_voice = switch_event_get_header(params, "tts_voice");
964
965 if ((position = switch_event_get_header(params, "position"))) {
966 if (conference->channels != 2) {
967 position = NULL;
968 } else {
969 channels = 1;
970 fnode->al = conference_al_create(pool);
971 if (conference_al_parse_position(fnode->al, position) != SWITCH_STATUS_SUCCESS) {
972 fnode->al = NULL;
973 channels = conference->channels;
974 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Position Data.\n");
975 }
976 }
977 }
978 }
979
980 if (conference->sh && conference->last_speech_channels != channels) {
981 switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
982 switch_core_speech_close(&conference->lsh, &flags);
983 conference->sh = NULL;
984 }
985
986 if (!tts_engine) {
987 tts_engine = conference->tts_engine;
988 }
989
990 if (!tts_voice) {
991 tts_voice = conference->tts_voice;
992 }
993
994 if (zstr(tts_engine) || zstr(tts_voice)) {
995 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing TTS engine or voice\n");
996 status = SWITCH_STATUS_FALSE;
997 goto end;
998 }
999
1000 if (!conference->sh) {
1001 memset(&conference->lsh, 0, sizeof(conference->lsh));
1002 if (switch_core_speech_open(&conference->lsh, tts_engine, tts_voice,
1003 conference->rate, conference->interval, channels, &flags, NULL) != SWITCH_STATUS_SUCCESS) {
1004 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid TTS module [%s]!\n", conference->tts_engine);
1005 status = SWITCH_STATUS_FALSE;
1006 goto end;
1007 }
1008 conference->last_speech_channels = channels;
1009 conference->sh = &conference->lsh;
1010 }
1011
1012 fnode->pool = pool;
1013
1014 /* Queue the node */
1015 switch_mutex_lock(conference->mutex);
1016 for (nptr = conference->fnode; nptr && nptr->next; nptr = nptr->next);
1017
1018 if (nptr) {
1019 nptr->next = fnode;
1020 } else {
1021 conference->fnode = fnode;
1022 }
1023
1024 fnode->sh = conference->sh;
1025 if (*text == '#') {
1026 char *tmp = (char *) text + 1;
1027 char *vp = tmp, voice[128] = "";
1028 if ((tmp = strchr(tmp, '#'))) {
1029 text = tmp + 1;
1030 switch_copy_string(voice, vp, (tmp - vp) + 1);
1031 switch_core_speech_text_param_tts(fnode->sh, "voice", voice);
1032 }
1033 } else {
1034 switch_core_speech_text_param_tts(fnode->sh, "voice", tts_voice);
1035 }
1036
1037 /* Begin Generation */
1038 switch_sleep(200000);
1039 switch_core_speech_feed_tts(fnode->sh, (char *) text, &flags);
1040 switch_mutex_unlock(conference->mutex);
1041 status = SWITCH_STATUS_SUCCESS;
1042
1043 end:
1044
1045 if (params) {
1046 switch_event_destroy(¶ms);
1047 }
1048
1049 return status;
1050 }
1051
1052
conference_list_conferences(const char * line,const char * cursor,switch_console_callback_match_t ** matches)1053 switch_status_t conference_list_conferences(const char *line, const char *cursor, switch_console_callback_match_t **matches)
1054 {
1055 switch_console_callback_match_t *my_matches = NULL;
1056 switch_status_t status = SWITCH_STATUS_FALSE;
1057 switch_hash_index_t *hi;
1058 void *val;
1059 const void *vvar;
1060
1061 switch_mutex_lock(conference_globals.hash_mutex);
1062 for (hi = switch_core_hash_first(conference_globals.conference_hash); hi; hi = switch_core_hash_next(&hi)) {
1063 switch_core_hash_this(hi, &vvar, NULL, &val);
1064 switch_console_push_match(&my_matches, (const char *) vvar);
1065 }
1066 switch_mutex_unlock(conference_globals.hash_mutex);
1067
1068 if (my_matches) {
1069 *matches = my_matches;
1070 status = SWITCH_STATUS_SUCCESS;
1071 }
1072
1073 return status;
1074 }
1075
conference_list_pretty(conference_obj_t * conference,switch_stream_handle_t * stream)1076 void conference_list_pretty(conference_obj_t *conference, switch_stream_handle_t *stream)
1077 {
1078 conference_member_t *member = NULL;
1079
1080 switch_assert(conference != NULL);
1081 switch_assert(stream != NULL);
1082
1083 switch_mutex_lock(conference->member_mutex);
1084
1085 for (member = conference->members; member; member = member->next) {
1086 switch_channel_t *channel;
1087 switch_caller_profile_t *profile;
1088
1089 if (conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
1090 continue;
1091 }
1092 channel = switch_core_session_get_channel(member->session);
1093 profile = switch_channel_get_caller_profile(channel);
1094
1095 stream->write_function(stream, "%u) %s (%s)\n", member->id, profile->caller_id_name, profile->caller_id_number);
1096 }
1097
1098 switch_mutex_unlock(conference->member_mutex);
1099 }
1100
1101
conference_list_count_only(conference_obj_t * conference,switch_stream_handle_t * stream)1102 void conference_list_count_only(conference_obj_t *conference, switch_stream_handle_t *stream)
1103 {
1104 switch_assert(conference != NULL);
1105 switch_assert(stream != NULL);
1106
1107 stream->write_function(stream, "%d", conference->count);
1108 }
1109
1110
add_x_tag(switch_xml_t x_member,const char * name,const char * value,int off)1111 switch_xml_t add_x_tag(switch_xml_t x_member, const char *name, const char *value, int off)
1112 {
1113 switch_size_t dlen;
1114 char *data;
1115 switch_xml_t x_tag;
1116
1117 if (!value) {
1118 return 0;
1119 }
1120
1121 dlen = strlen(value) * 3 + 1;
1122
1123 x_tag = switch_xml_add_child_d(x_member, name, off);
1124 switch_assert(x_tag);
1125
1126 switch_zmalloc(data, dlen);
1127
1128 switch_url_encode(value, data, dlen);
1129 switch_xml_set_txt_d(x_tag, data);
1130 free(data);
1131
1132 return x_tag;
1133 }
1134
conference_xlist(conference_obj_t * conference,switch_xml_t x_conference,int off)1135 void conference_xlist(conference_obj_t *conference, switch_xml_t x_conference, int off)
1136 {
1137 conference_member_t *member = NULL;
1138 switch_xml_t x_member = NULL, x_members = NULL, x_flags, x_variables;
1139 switch_event_header_t *hp;
1140 int moff = 0;
1141 char i[30] = "";
1142 char *ival = i;
1143 switch_assert(conference != NULL);
1144 switch_assert(x_conference != NULL);
1145
1146 switch_xml_set_attr_d(x_conference, "name", conference->name);
1147 switch_snprintf(i, sizeof(i), "%d", conference->count);
1148 switch_xml_set_attr_d(x_conference, "member-count", ival);
1149 switch_snprintf(i, sizeof(i), "%d", conference->count_ghosts);
1150 switch_xml_set_attr_d(x_conference, "ghost-count", ival);
1151 switch_snprintf(i, sizeof(i), "%u", conference->rate);
1152 switch_xml_set_attr_d(x_conference, "rate", ival);
1153 switch_xml_set_attr_d(x_conference, "uuid", conference->uuid_str);
1154
1155 if (conference_utils_test_flag(conference, CFLAG_LOCKED)) {
1156 switch_xml_set_attr_d(x_conference, "locked", "true");
1157 }
1158
1159 if (conference_utils_test_flag(conference, CFLAG_DESTRUCT)) {
1160 switch_xml_set_attr_d(x_conference, "destruct", "true");
1161 }
1162
1163 if (conference_utils_test_flag(conference, CFLAG_WAIT_MOD)) {
1164 switch_xml_set_attr_d(x_conference, "wait_mod", "true");
1165 }
1166
1167 if (conference_utils_test_flag(conference, CFLAG_AUDIO_ALWAYS)) {
1168 switch_xml_set_attr_d(x_conference, "audio_always", "true");
1169 }
1170
1171 if (conference_utils_test_flag(conference, CFLAG_RUNNING)) {
1172 switch_xml_set_attr_d(x_conference, "running", "true");
1173 }
1174
1175 if (conference_utils_test_flag(conference, CFLAG_ANSWERED)) {
1176 switch_xml_set_attr_d(x_conference, "answered", "true");
1177 }
1178
1179 if (conference_utils_test_flag(conference, CFLAG_ENFORCE_MIN)) {
1180 switch_xml_set_attr_d(x_conference, "enforce_min", "true");
1181 }
1182
1183 if (conference_utils_test_flag(conference, CFLAG_BRIDGE_TO)) {
1184 switch_xml_set_attr_d(x_conference, "bridge_to", "true");
1185 }
1186
1187 if (conference_utils_test_flag(conference, CFLAG_DYNAMIC)) {
1188 switch_xml_set_attr_d(x_conference, "dynamic", "true");
1189 }
1190
1191 if (conference_utils_test_flag(conference, CFLAG_EXIT_SOUND)) {
1192 switch_xml_set_attr_d(x_conference, "exit_sound", "true");
1193 }
1194
1195 if (conference_utils_test_flag(conference, CFLAG_ENTER_SOUND)) {
1196 switch_xml_set_attr_d(x_conference, "enter_sound", "true");
1197 }
1198
1199 if (conference->max_members > 0) {
1200 switch_snprintf(i, sizeof(i), "%d", conference->max_members);
1201 switch_xml_set_attr_d(x_conference, "max_members", ival);
1202 }
1203
1204 if (conference->record_count > 0) {
1205 switch_xml_set_attr_d(x_conference, "recording", "true");
1206 }
1207
1208 if (conference->endconference_grace_time > 0) {
1209 switch_snprintf(i, sizeof(i), "%u", conference->endconference_grace_time);
1210 switch_xml_set_attr_d(x_conference, "endconference_grace_time", ival);
1211 }
1212
1213 if (conference_utils_test_flag(conference, CFLAG_VID_FLOOR)) {
1214 switch_xml_set_attr_d(x_conference, "video_floor_only", "true");
1215 }
1216
1217 if (conference_utils_test_flag(conference, CFLAG_RFC4579)) {
1218 switch_xml_set_attr_d(x_conference, "video_rfc4579", "true");
1219 }
1220
1221 switch_snprintf(i, sizeof(i), "%d", switch_epoch_time_now(NULL) - conference->run_time);
1222 switch_xml_set_attr_d(x_conference, "run_time", ival);
1223
1224 x_variables = switch_xml_add_child_d(x_conference, "variables", 0);
1225 for (hp = conference->variables->headers; hp; hp = hp->next) {
1226 switch_xml_t x_variable = switch_xml_add_child_d(x_variables, "variable", 0);
1227 switch_xml_set_attr_d(x_variable, "name", hp->name);
1228 switch_xml_set_attr_d(x_variable, "value", hp->value);
1229 }
1230
1231 x_members = switch_xml_add_child_d(x_conference, "members", 0);
1232 switch_assert(x_members);
1233
1234 switch_mutex_lock(conference->member_mutex);
1235
1236 for (member = conference->members; member; member = member->next) {
1237 switch_channel_t *channel;
1238 switch_caller_profile_t *profile;
1239 char *uuid;
1240 //char *name;
1241 uint32_t count = 0;
1242 switch_xml_t x_tag;
1243 int toff = 0;
1244 char tmp[50] = "";
1245 switch_bool_t hold = conference_utils_member_test_flag(member, MFLAG_HOLD);
1246
1247 if (conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
1248 if (member->rec_path) {
1249 x_member = switch_xml_add_child_d(x_members, "member", moff++);
1250 switch_assert(x_member);
1251 switch_xml_set_attr_d(x_member, "type", "recording_node");
1252 /* or:
1253 x_member = switch_xml_add_child_d(x_members, "recording_node", moff++);
1254 */
1255
1256 x_tag = switch_xml_add_child_d(x_member, "record_path", count++);
1257 if (conference_utils_member_test_flag(member, MFLAG_PAUSE_RECORDING)) {
1258 switch_xml_set_attr_d(x_tag, "status", "paused");
1259 }
1260 switch_xml_set_txt_d(x_tag, member->rec_path);
1261
1262 x_tag = switch_xml_add_child_d(x_member, "join_time", count++);
1263 switch_xml_set_attr_d(x_tag, "type", "UNIX-epoch");
1264 switch_snprintf(i, sizeof(i), "%d", member->rec_time);
1265 switch_xml_set_txt_d(x_tag, i);
1266 }
1267 continue;
1268 }
1269
1270 uuid = switch_core_session_get_uuid(member->session);
1271 channel = switch_core_session_get_channel(member->session);
1272 profile = switch_channel_get_caller_profile(channel);
1273 //name = switch_channel_get_name(channel);
1274
1275
1276 x_member = switch_xml_add_child_d(x_members, "member", moff++);
1277 switch_assert(x_member);
1278 switch_xml_set_attr_d(x_member, "type", "caller");
1279
1280 switch_snprintf(i, sizeof(i), "%d", member->id);
1281
1282 add_x_tag(x_member, "id", i, toff++);
1283 add_x_tag(x_member, "uuid", uuid, toff++);
1284 add_x_tag(x_member, "caller_id_name", profile->caller_id_name, toff++);
1285 add_x_tag(x_member, "caller_id_number", profile->caller_id_number, toff++);
1286
1287
1288 switch_snprintf(i, sizeof(i), "%d", switch_epoch_time_now(NULL) - member->join_time);
1289 add_x_tag(x_member, "join_time", i, toff++);
1290
1291 switch_snprintf(i, sizeof(i), "%d", switch_epoch_time_now(NULL) - member->last_talking);
1292 add_x_tag(x_member, "last_talking", member->last_talking ? i : "N/A", toff++);
1293
1294 switch_snprintf(i, sizeof(i), "%d", member->energy_level);
1295 add_x_tag(x_member, "energy", i, toff++);
1296
1297 switch_snprintf(i, sizeof(i), "%d", member->volume_in_level);
1298 add_x_tag(x_member, "volume_in", i, toff++);
1299
1300 switch_snprintf(i, sizeof(i), "%d", member->volume_out_level);
1301 add_x_tag(x_member, "volume_out", i, toff++);
1302
1303 x_flags = switch_xml_add_child_d(x_member, "flags", count++);
1304 switch_assert(x_flags);
1305
1306 x_tag = switch_xml_add_child_d(x_flags, "can_hear", count++);
1307 switch_xml_set_txt_d(x_tag, (!hold && conference_utils_member_test_flag(member, MFLAG_CAN_HEAR)) ? "true" : "false");
1308
1309 x_tag = switch_xml_add_child_d(x_flags, "can_see", count++);
1310 switch_xml_set_txt_d(x_tag, (!hold && conference_utils_member_test_flag(member, MFLAG_CAN_SEE)) ? "true" : "false");
1311
1312 x_tag = switch_xml_add_child_d(x_flags, "can_speak", count++);
1313 switch_xml_set_txt_d(x_tag, (!hold && conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)) ? "true" : "false");
1314
1315 x_tag = switch_xml_add_child_d(x_flags, "mute_detect", count++);
1316 switch_xml_set_txt_d(x_tag, conference_utils_member_test_flag(member, MFLAG_MUTE_DETECT) ? "true" : "false");
1317
1318 x_tag = switch_xml_add_child_d(x_flags, "talking", count++);
1319 switch_xml_set_txt_d(x_tag, (!hold && conference_utils_member_test_flag(member, MFLAG_TALKING)) ? "true" : "false");
1320
1321 x_tag = switch_xml_add_child_d(x_flags, "hold", count++);
1322 switch_xml_set_txt_d(x_tag, hold ? "true" : "false");
1323
1324 x_tag = switch_xml_add_child_d(x_flags, "has_video", count++);
1325 switch_xml_set_txt_d(x_tag, switch_channel_test_flag(switch_core_session_get_channel(member->session), CF_VIDEO) ? "true" : "false");
1326
1327 x_tag = switch_xml_add_child_d(x_flags, "video_bridge", count++);
1328 switch_xml_set_txt_d(x_tag, conference_utils_member_test_flag(member, MFLAG_VIDEO_BRIDGE) ? "true" : "false");
1329
1330 x_tag = switch_xml_add_child_d(x_flags, "has_floor", count++);
1331 switch_xml_set_txt_d(x_tag, (member->id == member->conference->floor_holder) ? "true" : "false");
1332
1333 x_tag = switch_xml_add_child_d(x_flags, "is_moderator", count++);
1334 switch_xml_set_txt_d(x_tag, conference_utils_member_test_flag(member, MFLAG_MOD) ? "true" : "false");
1335
1336 x_tag = switch_xml_add_child_d(x_flags, "end_conference", count++);
1337 switch_xml_set_txt_d(x_tag, conference_utils_member_test_flag(member, MFLAG_ENDCONF) ? "true" : "false");
1338
1339 x_tag = switch_xml_add_child_d(x_flags, "is_ghost", count++);
1340 switch_xml_set_txt_d(x_tag, conference_utils_member_test_flag(member, MFLAG_GHOST) ? "true" : "false");
1341
1342 switch_snprintf(tmp, sizeof(tmp), "%d", member->volume_out_level);
1343 add_x_tag(x_member, "output-volume", tmp, toff++);
1344 }
1345
1346 switch_mutex_unlock(conference->member_mutex);
1347 }
1348
conference_jlist(conference_obj_t * conference,cJSON * json_conferences)1349 void conference_jlist(conference_obj_t *conference, cJSON *json_conferences)
1350 {
1351 conference_member_t *member = NULL;
1352 static cJSON *json_conference, *json_conference_variables, *json_conference_members, *json_conference_member, *json_conference_member_flags;
1353 switch_event_header_t *hp;
1354
1355 switch_assert(conference != NULL);
1356 json_conference = cJSON_CreateObject();
1357 switch_assert(json_conference);
1358
1359 cJSON_AddItemToObject(json_conferences, "conference", json_conference);
1360 cJSON_AddStringToObject(json_conference, "conference_name", conference->name);
1361 cJSON_AddNumberToObject(json_conference,"member_count", conference->count);
1362 cJSON_AddNumberToObject(json_conference,"ghost_count", conference->count_ghosts);
1363 cJSON_AddNumberToObject(json_conference,"rate", conference->rate);
1364 cJSON_AddNumberToObject(json_conference,"run_time", switch_epoch_time_now(NULL) - conference->run_time);
1365 cJSON_AddStringToObject(json_conference, "conference_uuid", conference->uuid_str);
1366 cJSON_AddNumberToObject(json_conference, "canvas_count", conference->canvas_count);
1367 cJSON_AddNumberToObject(json_conference, "max_bw_in", conference->max_bw_in);
1368 cJSON_AddNumberToObject(json_conference, "force_bw_in", conference->force_bw_in);
1369 cJSON_AddNumberToObject(json_conference, "video_floor_packets", conference->video_floor_packets);
1370
1371 #define ADDBOOL(obj, name, b) cJSON_AddItemToObject(obj, name, (b) ? cJSON_CreateTrue() : cJSON_CreateFalse())
1372
1373 ADDBOOL(json_conference, "locked", conference_utils_test_flag(conference, CFLAG_LOCKED));
1374 ADDBOOL(json_conference, "destruct", conference_utils_test_flag(conference, CFLAG_DESTRUCT));
1375 ADDBOOL(json_conference, "wait_mod", conference_utils_test_flag(conference, CFLAG_WAIT_MOD));
1376 ADDBOOL(json_conference, "audio_always", conference_utils_test_flag(conference, CFLAG_AUDIO_ALWAYS));
1377 ADDBOOL(json_conference, "running", conference_utils_test_flag(conference, CFLAG_RUNNING));
1378 ADDBOOL(json_conference, "answered", conference_utils_test_flag(conference, CFLAG_ANSWERED));
1379 ADDBOOL(json_conference, "enforce_min", conference_utils_test_flag(conference, CFLAG_ENFORCE_MIN));
1380 ADDBOOL(json_conference, "bridge_to", conference_utils_test_flag(conference, CFLAG_BRIDGE_TO));
1381 ADDBOOL(json_conference, "dynamic", conference_utils_test_flag(conference, CFLAG_DYNAMIC));
1382 ADDBOOL(json_conference, "exit_sound", conference_utils_test_flag(conference, CFLAG_EXIT_SOUND));
1383 ADDBOOL(json_conference, "enter_sound", conference_utils_test_flag(conference, CFLAG_ENTER_SOUND));
1384 ADDBOOL(json_conference, "recording", conference->record_count > 0);
1385 ADDBOOL(json_conference, "video_bridge", conference_utils_test_flag(conference, CFLAG_VIDEO_BRIDGE_FIRST_TWO));
1386 ADDBOOL(json_conference, "video_floor_only", conference_utils_test_flag(conference, CFLAG_VID_FLOOR));
1387 ADDBOOL(json_conference, "video_rfc4579", conference_utils_test_flag(conference, CFLAG_RFC4579));
1388
1389 if (conference->max_members > 0) {
1390 cJSON_AddNumberToObject(json_conference, "max_members", conference->max_members);
1391 }
1392
1393 cJSON_AddItemToObject(json_conference, "variables", json_conference_variables = cJSON_CreateObject());
1394 for (hp = conference->variables->headers; hp; hp = hp->next) {
1395 cJSON_AddStringToObject(json_conference_variables, hp->name, hp->value);
1396 }
1397
1398 cJSON_AddItemToObject(json_conference, "members", json_conference_members = cJSON_CreateArray());
1399 switch_mutex_lock(conference->member_mutex);
1400 for (member = conference->members; member; member = member->next) {
1401 switch_channel_t *channel;
1402 switch_caller_profile_t *profile;
1403 char *uuid;
1404 switch_bool_t hold = conference_utils_member_test_flag(member, MFLAG_HOLD);
1405
1406 cJSON_AddItemToObject(json_conference_members, "member", json_conference_member = cJSON_CreateObject());
1407
1408 if (conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
1409 if (member->rec_path) {
1410 cJSON_AddStringToObject(json_conference_member, "type", "recording_node");
1411 cJSON_AddStringToObject(json_conference_member, "record_path", member->rec_path);
1412 if (conference_utils_member_test_flag(member, MFLAG_PAUSE_RECORDING)) {
1413 cJSON_AddStringToObject(json_conference_member, "status", "paused");
1414 }
1415 cJSON_AddNumberToObject(json_conference_member, "join_time", member->rec_time);
1416 }
1417 continue;
1418 }
1419
1420 uuid = switch_core_session_get_uuid(member->session);
1421 channel = switch_core_session_get_channel(member->session);
1422 profile = switch_channel_get_caller_profile(channel);
1423
1424 cJSON_AddStringToObject(json_conference_member, "type", "caller");
1425 cJSON_AddNumberToObject(json_conference_member, "id", member->id);
1426 cJSON_AddItemToObject(json_conference_member, "flags", json_conference_member_flags = cJSON_CreateObject());
1427 cJSON_AddStringToObject(json_conference_member, "uuid", uuid);
1428 cJSON_AddStringToObject(json_conference_member, "caller_id_name", profile->caller_id_name);
1429 cJSON_AddStringToObject(json_conference_member,"caller_id_number", profile->caller_id_number);
1430 cJSON_AddNumberToObject(json_conference_member, "join_time", switch_epoch_time_now(NULL) - member->join_time);
1431 cJSON_AddNumberToObject(json_conference_member, "last_talking", member->last_talking ? switch_epoch_time_now(NULL) - member->last_talking : 0);
1432 cJSON_AddNumberToObject(json_conference_member, "energy", member->energy_level);
1433 cJSON_AddNumberToObject(json_conference_member, "volume_in", member->volume_in_level);
1434 cJSON_AddNumberToObject(json_conference_member, "volume_out", member->volume_out_level);
1435 cJSON_AddNumberToObject(json_conference_member, "output-volume", member->volume_out_level);
1436 cJSON_AddNumberToObject(json_conference_member, "input-volume", member->volume_in_level);
1437 ADDBOOL(json_conference_member_flags, "can_hear", !hold && conference_utils_member_test_flag(member, MFLAG_CAN_HEAR));
1438 ADDBOOL(json_conference_member_flags, "can_see", !hold && conference_utils_member_test_flag(member, MFLAG_CAN_SEE));
1439 ADDBOOL(json_conference_member_flags, "can_speak", !hold && conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK));
1440 ADDBOOL(json_conference_member_flags, "hold", hold);
1441 ADDBOOL(json_conference_member_flags, "mute_detect", conference_utils_member_test_flag(member, MFLAG_MUTE_DETECT));
1442 ADDBOOL(json_conference_member_flags, "talking", conference_utils_member_test_flag(member, MFLAG_TALKING));
1443 ADDBOOL(json_conference_member_flags, "has_video", switch_channel_test_flag(switch_core_session_get_channel(member->session), CF_VIDEO));
1444 ADDBOOL(json_conference_member_flags, "video_bridge", conference_utils_member_test_flag(member, MFLAG_VIDEO_BRIDGE));
1445 ADDBOOL(json_conference_member_flags, "has_floor", member->id == member->conference->floor_holder);
1446 ADDBOOL(json_conference_member_flags, "is_moderator", conference_utils_member_test_flag(member, MFLAG_MOD));
1447 ADDBOOL(json_conference_member_flags, "end_conference", conference_utils_member_test_flag(member, MFLAG_ENDCONF));
1448 }
1449 switch_mutex_unlock(conference->member_mutex);
1450 }
1451
conference_fnode_toggle_pause(conference_file_node_t * fnode,switch_stream_handle_t * stream)1452 void conference_fnode_toggle_pause(conference_file_node_t *fnode, switch_stream_handle_t *stream)
1453 {
1454 if (fnode) {
1455 switch_core_file_command(&fnode->fh, SCFC_PAUSE_READ);
1456 if (switch_test_flag(fnode, NFLAG_PAUSE)) {
1457 stream->write_function(stream, "+OK Resume\n");
1458 switch_clear_flag(fnode, NFLAG_PAUSE);
1459 } else {
1460 stream->write_function(stream, "+OK Pause\n");
1461 switch_set_flag(fnode, NFLAG_PAUSE);
1462 }
1463 }
1464 }
1465
conference_fnode_check_status(conference_file_node_t * fnode,switch_stream_handle_t * stream)1466 void conference_fnode_check_status(conference_file_node_t *fnode, switch_stream_handle_t *stream)
1467 {
1468 if (fnode) {
1469 stream->write_function(stream, "+OK %"SWITCH_INT64_T_FMT "/%" SWITCH_INT64_T_FMT " %s\n",
1470 fnode->fh.vpos, fnode->fh.duration, fnode->fh.file_path);
1471 } else {
1472 stream->write_function(stream, "-ERR Nothing is playing\n");
1473 }
1474 }
1475
conference_fnode_seek(conference_file_node_t * fnode,switch_stream_handle_t * stream,char * arg)1476 void conference_fnode_seek(conference_file_node_t *fnode, switch_stream_handle_t *stream, char *arg)
1477 {
1478 if (fnode && fnode->type == NODE_TYPE_FILE) {
1479 unsigned int samps = 0;
1480 unsigned int pos = 0;
1481
1482 if (*arg == '+' || *arg == '-') {
1483 int step;
1484 int32_t target;
1485 if (!(step = atoi(arg))) {
1486 step = 1000;
1487 }
1488
1489 samps = step * (fnode->fh.native_rate / 1000);
1490 target = (int32_t)fnode->fh.pos + samps;
1491
1492 if (target < 0) {
1493 target = 0;
1494 }
1495
1496 stream->write_function(stream, "+OK seek to position %d\n", target);
1497 switch_core_file_seek(&fnode->fh, &pos, target, SEEK_SET);
1498
1499 } else {
1500 samps = switch_atoui(arg) * (fnode->fh.native_rate / 1000);
1501 stream->write_function(stream, "+OK seek to position %d\n", samps);
1502 switch_core_file_seek(&fnode->fh, &pos, samps, SEEK_SET);
1503 }
1504 }
1505 }
1506
1507
1508 /* generate an outbound call from the conference */
conference_outcall(conference_obj_t * conference,char * conference_name,switch_core_session_t * session,char * bridgeto,uint32_t timeout,char * flags,char * cid_name,char * cid_num,char * profile,switch_call_cause_t * cause,switch_call_cause_t * cancel_cause,switch_event_t * var_event,char ** peer_uuid)1509 switch_status_t conference_outcall(conference_obj_t *conference,
1510 char *conference_name,
1511 switch_core_session_t *session,
1512 char *bridgeto, uint32_t timeout,
1513 char *flags, char *cid_name,
1514 char *cid_num,
1515 char *profile,
1516 switch_call_cause_t *cause,
1517 switch_call_cause_t *cancel_cause,
1518 switch_event_t *var_event,
1519 char** peer_uuid
1520 )
1521 {
1522 switch_core_session_t *peer_session = NULL;
1523 switch_channel_t *peer_channel;
1524 switch_status_t status = SWITCH_STATUS_SUCCESS;
1525 switch_channel_t *caller_channel = NULL;
1526 char appdata[512];
1527 int rdlock = 0;
1528 switch_bool_t have_flags = SWITCH_FALSE;
1529 const char *outcall_flags;
1530 int track = 0;
1531 const char *call_id = NULL;
1532
1533 if (var_event && switch_true(switch_event_get_header(var_event, "conference_track_status"))) {
1534 track++;
1535 call_id = switch_event_get_header(var_event, "conference_track_call_id");
1536 }
1537
1538 *cause = SWITCH_CAUSE_NORMAL_CLEARING;
1539
1540 if (conference == NULL) {
1541 char *dialstr = switch_mprintf("{ignore_early_media=true}%s", bridgeto);
1542 status = switch_ivr_originate(NULL, &peer_session, cause, dialstr, 60, NULL, cid_name, cid_num, NULL, var_event, SOF_NO_LIMITS, NULL, NULL);
1543 switch_safe_free(dialstr);
1544
1545 if (status != SWITCH_STATUS_SUCCESS) {
1546 return status;
1547 }
1548
1549 peer_channel = switch_core_session_get_channel(peer_session);
1550 rdlock = 1;
1551 goto callup;
1552 }
1553
1554 conference_name = conference->name;
1555
1556 if (switch_thread_rwlock_tryrdlock(conference->rwlock) != SWITCH_STATUS_SUCCESS) {
1557 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Read Lock Fail\n");
1558 return SWITCH_STATUS_FALSE;
1559 }
1560
1561 if (session != NULL) {
1562 caller_channel = switch_core_session_get_channel(session);
1563 }
1564
1565 if (zstr(cid_name)) {
1566 cid_name = conference->caller_id_name;
1567 }
1568
1569 if (zstr(cid_num)) {
1570 cid_num = conference->caller_id_number;
1571 }
1572
1573 /* establish an outbound call leg */
1574
1575 switch_mutex_lock(conference->mutex);
1576 conference->originating++;
1577 switch_mutex_unlock(conference->mutex);
1578
1579 if (track) {
1580 conference_send_notify(conference, "SIP/2.0 100 Trying\r\n", call_id, SWITCH_FALSE);
1581 }
1582
1583
1584 status = switch_ivr_originate(session, &peer_session, cause, bridgeto, timeout, NULL, cid_name, cid_num, NULL, var_event, SOF_NO_LIMITS, cancel_cause, NULL);
1585 switch_mutex_lock(conference->mutex);
1586 conference->originating--;
1587 switch_mutex_unlock(conference->mutex);
1588
1589 if (status != SWITCH_STATUS_SUCCESS) {
1590 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot create outgoing channel, cause: %s\n",
1591 switch_channel_cause2str(*cause));
1592 if (caller_channel) {
1593 switch_channel_hangup(caller_channel, *cause);
1594 }
1595
1596 if (track) {
1597 conference_send_notify(conference, "SIP/2.0 481 Failure\r\n", call_id, SWITCH_TRUE);
1598 }
1599
1600 goto done;
1601 }
1602
1603 if (track) {
1604 conference_send_notify(conference, "SIP/2.0 200 OK\r\n", call_id, SWITCH_TRUE);
1605 }
1606
1607 rdlock = 1;
1608 peer_channel = switch_core_session_get_channel(peer_session);
1609
1610 /* make sure the conference still exists */
1611 if (!conference_utils_test_flag(conference, CFLAG_RUNNING)) {
1612 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Conference is gone now, nevermind..\n");
1613 if (caller_channel) {
1614 switch_channel_hangup(caller_channel, SWITCH_CAUSE_NO_ROUTE_DESTINATION);
1615 }
1616 switch_channel_hangup(peer_channel, SWITCH_CAUSE_NO_ROUTE_DESTINATION);
1617 goto done;
1618 }
1619
1620 if (caller_channel && switch_channel_test_flag(peer_channel, CF_ANSWERED)) {
1621 switch_channel_answer(caller_channel);
1622 }
1623
1624 callup:
1625
1626 /* if the outbound call leg is ready */
1627 if (switch_channel_test_flag(peer_channel, CF_ANSWERED) || switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA)) {
1628 switch_caller_extension_t *extension = NULL;
1629
1630 if(peer_uuid) {
1631 *peer_uuid = switch_channel_get_uuid(peer_channel);
1632 }
1633
1634 /* build an extension name object */
1635 if ((extension = switch_caller_extension_new(peer_session, conference_name, conference_name)) == 0) {
1636 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Memory Error!\n");
1637 status = SWITCH_STATUS_MEMERR;
1638 goto done;
1639 }
1640
1641 if ((outcall_flags = switch_channel_get_variable(peer_channel, "outcall_flags"))) {
1642 if (!zstr(outcall_flags)) {
1643 flags = (char *)outcall_flags;
1644 }
1645 }
1646
1647 if (flags && strcasecmp(flags, "none")) {
1648 have_flags = SWITCH_TRUE;
1649 }
1650 /* add them to the conference */
1651
1652 switch_snprintf(appdata, sizeof(appdata), "%s%s%s%s%s%s", conference_name,
1653 profile?"@":"", profile?profile:"",
1654 have_flags?"+flags{":"", have_flags?flags:"", have_flags?"}":"");
1655 switch_caller_extension_add_application(peer_session, extension, (char *) mod_conference_app_name, appdata);
1656
1657 switch_channel_set_caller_extension(peer_channel, extension);
1658 switch_channel_set_state(peer_channel, CS_EXECUTE);
1659
1660 } else {
1661 switch_channel_hangup(peer_channel, SWITCH_CAUSE_NO_ANSWER);
1662 status = SWITCH_STATUS_FALSE;
1663 goto done;
1664 }
1665
1666 done:
1667 if (conference) {
1668 switch_thread_rwlock_unlock(conference->rwlock);
1669 }
1670 if (rdlock && peer_session) {
1671 switch_core_session_rwunlock(peer_session);
1672 }
1673
1674 return status;
1675 }
1676
conference_outcall_run(switch_thread_t * thread,void * obj)1677 void *SWITCH_THREAD_FUNC conference_outcall_run(switch_thread_t *thread, void *obj)
1678 {
1679 struct bg_call *call = (struct bg_call *) obj;
1680 char* peer_uuid = NULL;
1681
1682 if (call) {
1683 switch_call_cause_t cause;
1684 switch_event_t *event;
1685
1686
1687 conference_outcall(call->conference, call->conference_name,
1688 call->session, call->bridgeto, call->timeout,
1689 call->flags, call->cid_name, call->cid_num, call->profile, &cause, call->cancel_cause, call->var_event, &peer_uuid);
1690
1691 if (call->conference && test_eflag(call->conference, EFLAG_BGDIAL_RESULT) &&
1692 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1693 conference_event_add_data(call->conference, event);
1694 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "bgdial-result");
1695 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Result", switch_channel_cause2str(cause));
1696 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-UUID", call->uuid);
1697 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Peer-UUID", peer_uuid);
1698 switch_event_fire(&event);
1699 }
1700
1701 if (call->var_event) {
1702 switch_event_destroy(&call->var_event);
1703 }
1704
1705 switch_safe_free(call->bridgeto);
1706 switch_safe_free(call->flags);
1707 switch_safe_free(call->cid_name);
1708 switch_safe_free(call->cid_num);
1709 switch_safe_free(call->conference_name);
1710 switch_safe_free(call->uuid);
1711 switch_safe_free(call->profile);
1712 if (call->pool) {
1713 switch_core_destroy_memory_pool(&call->pool);
1714 }
1715 switch_safe_free(call);
1716 }
1717
1718 return NULL;
1719 }
1720
conference_outcall_bg(conference_obj_t * conference,char * conference_name,switch_core_session_t * session,char * bridgeto,uint32_t timeout,const char * flags,const char * cid_name,const char * cid_num,const char * call_uuid,const char * profile,switch_call_cause_t * cancel_cause,switch_event_t ** var_event)1721 switch_status_t conference_outcall_bg(conference_obj_t *conference,
1722 char *conference_name,
1723 switch_core_session_t *session, char *bridgeto, uint32_t timeout, const char *flags, const char *cid_name,
1724 const char *cid_num, const char *call_uuid, const char *profile, switch_call_cause_t *cancel_cause, switch_event_t **var_event)
1725 {
1726 struct bg_call *call = NULL;
1727 switch_thread_t *thread;
1728 switch_threadattr_t *thd_attr = NULL;
1729 switch_memory_pool_t *pool = NULL;
1730
1731 if (!(call = malloc(sizeof(*call))))
1732 return SWITCH_STATUS_MEMERR;
1733
1734 memset(call, 0, sizeof(*call));
1735 call->conference = conference;
1736 call->session = session;
1737 call->timeout = timeout;
1738 call->cancel_cause = cancel_cause;
1739
1740 if (var_event) {
1741 call->var_event = *var_event;
1742 var_event = NULL;
1743 } else {
1744 switch_event_create_plain(&call->var_event, SWITCH_EVENT_GENERAL);
1745 }
1746
1747 if (conference) {
1748 pool = conference->pool;
1749 } else {
1750 switch_core_new_memory_pool(&pool);
1751 call->pool = pool;
1752 }
1753
1754 if (bridgeto) {
1755 call->bridgeto = strdup(bridgeto);
1756 }
1757 if (flags) {
1758 call->flags = strdup(flags);
1759 }
1760 if (cid_name) {
1761 call->cid_name = strdup(cid_name);
1762 }
1763 if (cid_num) {
1764 call->cid_num = strdup(cid_num);
1765 }
1766
1767 if (conference_name) {
1768 call->conference_name = strdup(conference_name);
1769 }
1770
1771 if (call_uuid) {
1772 call->uuid = strdup(call_uuid);
1773 if (call->var_event) {
1774 switch_event_add_header_string(call->var_event, SWITCH_STACK_BOTTOM, "conference_bgdial_jobid", call->uuid);
1775 }
1776 }
1777
1778 if (profile) {
1779 call->profile = strdup(profile);
1780 }
1781
1782 switch_threadattr_create(&thd_attr, pool);
1783 switch_threadattr_detach_set(thd_attr, 1);
1784 switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
1785 switch_thread_create(&thread, thd_attr, conference_outcall_run, call, pool);
1786 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Launching BG Thread for outcall\n");
1787
1788 return SWITCH_STATUS_SUCCESS;
1789 }
1790
1791
1792
SWITCH_STANDARD_APP(conference_auto_function)1793 SWITCH_STANDARD_APP(conference_auto_function)
1794 {
1795 switch_channel_t *channel = switch_core_session_get_channel(session);
1796 call_list_t *call_list, *np;
1797
1798 call_list = switch_channel_get_private(channel, "_conference_autocall_list_");
1799
1800 if (zstr(data)) {
1801 call_list = NULL;
1802 } else {
1803 np = switch_core_session_alloc(session, sizeof(*np));
1804 switch_assert(np != NULL);
1805
1806 np->string = switch_core_session_strdup(session, data);
1807 if (call_list) {
1808 np->next = call_list;
1809 np->iteration = call_list->iteration + 1;
1810 } else {
1811 np->iteration = 1;
1812 }
1813 call_list = np;
1814 }
1815 switch_channel_set_private(channel, "_conference_autocall_list_", call_list);
1816 }
1817
1818
conference_text_thread_callback(switch_core_session_t * session,switch_frame_t * frame,void * user_data)1819 switch_status_t conference_text_thread_callback(switch_core_session_t *session, switch_frame_t *frame, void *user_data)
1820 {
1821 conference_member_t *member = (conference_member_t *)user_data;
1822 switch_channel_t *channel = switch_core_session_get_channel(session);
1823 switch_size_t inuse = 0;
1824
1825 if (!member) return SWITCH_STATUS_FALSE;
1826
1827
1828 switch_mutex_lock(member->text_mutex);
1829 if (!member->text_buffer) {
1830 switch_buffer_create_dynamic(&member->text_buffer, 512, 1024, 0);
1831 switch_zmalloc(member->text_framedata, 1024);
1832 member->text_framesize = 1024;
1833 }
1834
1835 if (frame->data && frame->datalen && !(frame->flags & SFF_CNG)) {
1836 switch_buffer_write(member->text_buffer, frame->data, frame->datalen);
1837 }
1838
1839 inuse = switch_buffer_inuse(member->text_buffer);
1840
1841 if (zstr(member->text_framedata) && inuse && (switch_channel_test_flag(channel, CF_TEXT_IDLE) || switch_test_flag(frame, SFF_TEXT_LINE_BREAK))) {
1842 int bytes = 0;//, ok = 0;
1843
1844 if (inuse + 1 > member->text_framesize) {
1845 void *tmp = malloc(inuse + 1024);
1846 switch_assert(tmp);
1847 memcpy(tmp, member->text_framedata, member->text_framesize);
1848
1849 switch_assert(tmp);
1850
1851 member->text_framesize = inuse + 1024;
1852
1853 free(member->text_framedata);
1854 member->text_framedata = tmp;
1855
1856 }
1857
1858 bytes = switch_buffer_read(member->text_buffer, member->text_framedata, inuse);
1859 *(member->text_framedata + bytes) = '\0';
1860
1861 /*
1862 for(p = member->text_framedata; p && *p; p++) {
1863 if (*p > 32 && *p < 127) {
1864 ok++;
1865 }
1866 }
1867
1868 if (!ok) {
1869 member->text_framedata[0] = '\0';
1870 }
1871 */
1872 }
1873
1874 switch_mutex_unlock(member->text_mutex);
1875
1876 return SWITCH_STATUS_SUCCESS;
1877 }
1878
1879 /* Application interface function that is called from the dialplan to join the channel to a conference */
SWITCH_STANDARD_APP(conference_function)1880 SWITCH_STANDARD_APP(conference_function)
1881 {
1882 switch_codec_t *read_codec = NULL;
1883 //uint32_t flags = 0;
1884 conference_member_t member = { 0 };
1885 conference_obj_t *conference = NULL;
1886 switch_channel_t *channel = switch_core_session_get_channel(session);
1887 char *mydata = NULL;
1888 char *conference_name = NULL;
1889 char *bridge_prefix = "bridge:";
1890 char *flags_prefix = "+flags{";
1891 char *bridgeto = NULL;
1892 char *profile_name = NULL;
1893 switch_xml_t cxml = NULL, cfg = NULL, profiles = NULL;
1894 const char *flags_str, *v_flags_str;
1895 const char *cflags_str, *v_cflags_str;
1896 member_flag_t mflags[MFLAG_MAX] = { 0 };
1897 switch_core_session_message_t msg = { 0 };
1898 uint8_t rl = 0, isbr = 0;
1899 char *dpin = "";
1900 const char *mdpin = "";
1901 conference_xml_cfg_t xml_cfg = { 0 };
1902 switch_event_t *params = NULL;
1903 int locked = 0;
1904 int mpin_matched = 0;
1905 uint32_t *mid;
1906
1907 if (!switch_channel_test_app_flag_key("conference_silent", channel, CONF_SILENT_DONE) &&
1908 (switch_channel_test_flag(channel, CF_RECOVERED) || switch_true(switch_channel_get_variable(channel, "conference_silent_entry")))) {
1909 switch_channel_set_app_flag_key("conference_silent", channel, CONF_SILENT_REQ);
1910 }
1911
1912 switch_core_session_video_reset(session);
1913
1914 switch_channel_set_flag(channel, CF_CONFERENCE);
1915
1916 if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) {
1917 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Channel pre answer failed.\n");
1918 goto end;
1919 }
1920
1921 /* Save the original read codec. */
1922 if (!(read_codec = switch_core_session_get_read_codec(session))) {
1923 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Channel has no media!\n");
1924 goto end;
1925 }
1926
1927
1928 if (zstr(data)) {
1929 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Invalid arguments\n");
1930 goto end;
1931 }
1932
1933 mydata = switch_core_session_strdup(session, data);
1934
1935 if (!mydata) {
1936 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Pool Failure\n");
1937 goto end;
1938 }
1939
1940 if ((flags_str = strstr(mydata, flags_prefix))) {
1941 char *p;
1942 *((char *) flags_str) = '\0';
1943 flags_str += strlen(flags_prefix);
1944 if ((p = strchr(flags_str, '}'))) {
1945 *p = '\0';
1946 }
1947 }
1948
1949 //if ((v_flags_str = switch_channel_get_variable(channel, "conference_member_flags"))) {
1950 if ((v_flags_str = conference_utils_combine_flag_var(session, "conference_member_flags"))) {
1951 if (zstr(flags_str)) {
1952 flags_str = v_flags_str;
1953 } else {
1954 flags_str = switch_core_session_sprintf(session, "%s|%s", flags_str, v_flags_str);
1955 }
1956 }
1957
1958 cflags_str = flags_str;
1959
1960 //if ((v_cflags_str = switch_channel_get_variable(channel, "conference_flags"))) {
1961 if ((v_cflags_str = conference_utils_combine_flag_var(session, "conference_flags"))) {
1962 if (zstr(cflags_str)) {
1963 cflags_str = v_cflags_str;
1964 } else {
1965 cflags_str = switch_core_session_sprintf(session, "%s|%s", cflags_str, v_cflags_str);
1966 }
1967 }
1968
1969 /* is this a bridging conference ? */
1970 if (!strncasecmp(mydata, bridge_prefix, strlen(bridge_prefix))) {
1971 isbr = 1;
1972 mydata += strlen(bridge_prefix);
1973 if ((bridgeto = strchr(mydata, ':'))) {
1974 *bridgeto++ = '\0';
1975 } else {
1976 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Config Error!\n");
1977 goto done;
1978 }
1979 }
1980
1981 conference_name = mydata;
1982
1983 /* eat all leading spaces on conference name, which can cause problems */
1984 while (*conference_name == ' ') {
1985 conference_name++;
1986 }
1987
1988 /* is there a conference pin ? */
1989 if ((dpin = strchr(conference_name, '+'))) {
1990 *dpin++ = '\0';
1991 } else dpin = "";
1992
1993 /* is there profile specification ? */
1994 if ((profile_name = strrchr(conference_name, '@'))) {
1995 *profile_name++ = '\0';
1996 } else {
1997 profile_name = "default";
1998 }
1999
2000 #if 0
2001 if (0) {
2002 member.dtmf_parser = conference->dtmf_parser;
2003 } else {
2004
2005 }
2006 #endif
2007
2008 if (switch_channel_test_flag(channel, CF_RECOVERED)) {
2009 const char *check = switch_channel_get_variable(channel, "last_transfered_conference");
2010
2011 if (!zstr(check)) {
2012 conference_name = (char *) check;
2013 }
2014 }
2015
2016 switch_event_create(¶ms, SWITCH_EVENT_COMMAND);
2017 switch_assert(params);
2018 switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "conference_name", conference_name);
2019 switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "profile_name", profile_name);
2020 switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "Fetch-Call-UUID", switch_core_session_get_uuid(session));
2021
2022 /* Open the config from the xml registry */
2023 if (!(cxml = switch_xml_open_cfg(mod_conference_cf_name, &cfg, params))) {
2024 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Open of %s failed\n", mod_conference_cf_name);
2025 goto done;
2026 }
2027
2028 if ((profiles = switch_xml_child(cfg, "profiles"))) {
2029 xml_cfg.profile = switch_xml_find_child(profiles, "profile", "name", profile_name);
2030 }
2031
2032 /* if this is a bridging call, and it's not a duplicate, build a */
2033 /* conference object, and skip pin handling, and locked checking */
2034
2035 switch_mutex_lock(conference_globals.setup_mutex);
2036 locked = 1;
2037
2038 if (isbr) {
2039 char *uuid = switch_core_session_get_uuid(session);
2040
2041 if (!strcmp(conference_name, "_uuid_")) {
2042 conference_name = uuid;
2043 }
2044
2045 if ((conference = conference_find(conference_name, NULL))) {
2046 switch_thread_rwlock_unlock(conference->rwlock);
2047 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Conference %s already exists!\n", conference_name);
2048 goto done;
2049 }
2050
2051 /* Create the conference object. */
2052 conference = conference_new(conference_name, xml_cfg, session, NULL);
2053
2054 if (!conference) {
2055 goto done;
2056 }
2057
2058 conference->flags[CFLAG_JSON_STATUS] = 1;
2059 conference_utils_set_cflags(cflags_str, conference->flags);
2060
2061 switch_mutex_unlock(conference_globals.setup_mutex);
2062 locked = 0;
2063
2064 switch_channel_set_variable(channel, "conference_name", conference->name);
2065 switch_channel_set_variable(channel, SWITCH_RFC7989_APP_SESSION_ID_VARIABLE, conference->uuid_str);
2066
2067 /* Set the minimum number of members (once you go above it you cannot go below it) */
2068 conference->min = 2;
2069
2070 /* Indicate the conference is dynamic */
2071 conference_utils_set_flag_locked(conference, CFLAG_DYNAMIC);
2072
2073 /* Indicate the conference has a bridgeto party */
2074 conference_utils_set_flag_locked(conference, CFLAG_BRIDGE_TO);
2075
2076 /* Start the conference thread for this conference */
2077 conference_launch_thread(conference);
2078
2079 switch_channel_api_on(channel, "api_on_conference_create");
2080 } else {
2081 int enforce_security = switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND;
2082 const char *pvar = switch_channel_get_variable(channel, "conference_enforce_security");
2083
2084 if (pvar) {
2085 enforce_security = switch_true(pvar);
2086 }
2087
2088 if ((conference = conference_find(conference_name, NULL))) {
2089 switch_mutex_unlock(conference_globals.setup_mutex);
2090 locked = 0;
2091 }
2092
2093 /* if the conference exists, get the pointer to it */
2094 if (!conference) {
2095 const char *max_members_str;
2096 const char *endconference_grace_time_str;
2097 const char *auto_record_str;
2098
2099 /* no conference yet, so check for join-only flag */
2100 if (flags_str) {
2101 conference_utils_set_mflags(flags_str, mflags);
2102
2103 if (!(mflags[MFLAG_CAN_SPEAK])) {
2104 if (!(mflags[MFLAG_MUTE_DETECT])) {
2105 switch_core_media_hard_mute(session, SWITCH_TRUE);
2106 }
2107 }
2108
2109 if (mflags[MFLAG_JOIN_ONLY]) {
2110 switch_event_t *event;
2111 switch_xml_t jos_xml;
2112 char *val;
2113 /* send event */
2114 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT);
2115 switch_channel_event_set_basic_data(channel, event);
2116 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference_name);
2117 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Conference-Profile-Name", profile_name);
2118 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "rejected-join-only");
2119 switch_event_fire(&event);
2120 /* check what sound file to play */
2121 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Cannot create a conference since join-only flag is set\n");
2122 jos_xml = switch_xml_find_child(xml_cfg.profile, "param", "name", "join-only-sound");
2123 if (jos_xml && (val = (char *) switch_xml_attr_soft(jos_xml, "value"))) {
2124 switch_channel_answer(channel);
2125 switch_ivr_play_file(session, NULL, val, NULL);
2126 }
2127 if (!switch_false(switch_channel_get_variable(channel, "hangup_after_conference"))) {
2128 switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
2129 }
2130 goto done;
2131 }
2132 }
2133
2134 /* couldn't find the conference, create one */
2135 conference = conference_new(conference_name, xml_cfg, session, NULL);
2136
2137 if (!conference) {
2138 goto done;
2139 }
2140
2141 conference->flags[CFLAG_JSON_STATUS] = 1;
2142 conference_utils_set_cflags(cflags_str, conference->flags);
2143
2144 if (locked) {
2145 switch_mutex_unlock(conference_globals.setup_mutex);
2146 locked = 0;
2147 }
2148
2149 switch_channel_set_variable(channel, "conference_name", conference->name);
2150 switch_channel_set_variable(channel, SWITCH_RFC7989_APP_SESSION_ID_VARIABLE, conference->uuid_str);
2151
2152 /* Set MOH from variable if not set */
2153 if (zstr(conference->moh_sound)) {
2154 conference->moh_sound = switch_core_strdup(conference->pool, switch_channel_get_variable(channel, "conference_moh_sound"));
2155 }
2156
2157 /* Set perpetual-sound from variable if not set */
2158 if (zstr(conference->perpetual_sound)) {
2159 conference->perpetual_sound = switch_core_strdup(conference->pool, switch_channel_get_variable(channel, "conference_perpetual_sound"));
2160 }
2161
2162 /* Override auto-record profile parameter from variable */
2163 if (!zstr(auto_record_str = switch_channel_get_variable(channel, "conference_auto_record"))) {
2164 conference->auto_record = switch_core_strdup(conference->pool, auto_record_str);
2165 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
2166 "conference_auto_record set from variable to %s\n", auto_record_str);
2167 }
2168
2169 /* Set the minimum number of members (once you go above it you cannot go below it) */
2170 conference->min = 1;
2171
2172 /* check for variable used to specify override for max_members */
2173 if (!zstr(max_members_str = switch_channel_get_variable(channel, "conference_max_members"))) {
2174 uint32_t max_members_val;
2175 errno = 0; /* sanity first */
2176 max_members_val = strtol(max_members_str, NULL, 0); /* base 0 lets 0x... for hex 0... for octal and base 10 otherwise through */
2177 if (errno == ERANGE || errno == EINVAL || (int32_t) max_members_val < 0 || max_members_val == 1) {
2178 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
2179 "conference_max_members variable %s is invalid, not setting a limit\n", max_members_str);
2180 } else {
2181 conference->max_members = max_members_val;
2182 }
2183 }
2184
2185 /* check for variable to override endconference_grace_time profile value */
2186 if (!zstr(endconference_grace_time_str = switch_channel_get_variable(channel, "conference_endconference_grace_time"))) {
2187 uint32_t grace_time_val;
2188 errno = 0; /* sanity first */
2189 grace_time_val = strtol(endconference_grace_time_str, NULL, 0); /* base 0 lets 0x... for hex 0... for octal and base 10 otherwise through */
2190 if (errno == ERANGE || errno == EINVAL || (int32_t) grace_time_val < 0) {
2191 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
2192 "conference_endconference_grace_time variable %s is invalid, not setting a time limit\n", endconference_grace_time_str);
2193 } else {
2194 conference->endconference_grace_time = grace_time_val;
2195 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
2196 "conference endconference_grace_time set from variable to %d\n", grace_time_val);
2197 }
2198 }
2199
2200 /* Indicate the conference is dynamic */
2201 conference_utils_set_flag_locked(conference, CFLAG_DYNAMIC);
2202
2203 /* acquire a read lock on the thread so it can't leave without us */
2204 if (switch_thread_rwlock_tryrdlock(conference->rwlock) != SWITCH_STATUS_SUCCESS) {
2205 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Read Lock Fail\n");
2206 goto done;
2207 }
2208
2209 rl++;
2210
2211 /* Start the conference thread for this conference */
2212 conference_launch_thread(conference);
2213
2214 switch_channel_api_on(channel, "api_on_conference_create");
2215 } else { /* setup user variable */
2216 switch_channel_set_variable(channel, "conference_name", conference->name);
2217 switch_channel_set_variable(channel, SWITCH_RFC7989_APP_SESSION_ID_VARIABLE, conference->uuid_str);
2218 rl++;
2219 }
2220
2221 /* Moderator PIN as a channel variable */
2222 mdpin = switch_channel_get_variable(channel, "conference_moderator_pin");
2223
2224 if (zstr(dpin) && conference->pin) {
2225 dpin = conference->pin;
2226 }
2227 if (zstr(mdpin) && conference->mpin) {
2228 mdpin = conference->mpin;
2229 }
2230
2231 /* Tell the channel we have a new Session-ID */
2232 msg.from = __FILE__;
2233 msg.message_id = SWITCH_MESSAGE_INDICATE_SESSION_ID;
2234 switch_core_session_receive_message(session, &msg);
2235
2236 switch_channel_answer(channel);
2237
2238 /* if this is not an outbound call, deal with conference pins */
2239 if (enforce_security && (!zstr(dpin) || !zstr(mdpin))) {
2240 char pin_buf[80] = "";
2241 char *cf_pin_url_param_name = "X-ConfPin=";
2242 int pin_retries = conference->pin_retries;
2243 int pin_valid = 0;
2244 switch_status_t status = SWITCH_STATUS_SUCCESS;
2245 char *supplied_pin_value;
2246
2247 /* look for PIN in channel variable first. If not present or invalid revert to prompting user */
2248 supplied_pin_value = switch_core_strdup(conference->pool, switch_channel_get_variable(channel, "supplied_pin"));
2249 if (!zstr(supplied_pin_value)) {
2250 char *supplied_pin_value_start;
2251 int i = 0;
2252 if ((supplied_pin_value_start = (char *) switch_stristr(cf_pin_url_param_name, supplied_pin_value))) {
2253 /* pin supplied as a URL parameter, move pointer to start of actual pin value */
2254 supplied_pin_value = supplied_pin_value_start + strlen(cf_pin_url_param_name);
2255 }
2256 while (*supplied_pin_value != 0 && *supplied_pin_value != ';') {
2257 pin_buf[i++] = *supplied_pin_value++;
2258 }
2259
2260 validate_pin(pin_buf, dpin, mdpin);
2261 memset(pin_buf, 0, sizeof(pin_buf));
2262 }
2263
2264 if (!conference->pin_sound) {
2265 conference->pin_sound = switch_core_strdup(conference->pool, "conference/conf-pin.wav");
2266 }
2267
2268 if (!conference->bad_pin_sound) {
2269 conference->bad_pin_sound = switch_core_strdup(conference->pool, "conference/conf-bad-pin.wav");
2270 }
2271
2272 while (!pin_valid && pin_retries && status == SWITCH_STATUS_SUCCESS) {
2273 size_t dpin_length = strlen(dpin);
2274 size_t mdpin_length = mdpin ? strlen(mdpin) : 0;
2275 int maxpin = dpin_length > mdpin_length ? (int)dpin_length : (int)mdpin_length;
2276 switch_status_t pstatus = SWITCH_STATUS_FALSE;
2277
2278 /* be friendly */
2279 if (conference->pin_sound) {
2280 pstatus = conference_file_local_play(conference, session, conference->pin_sound, 20, pin_buf, sizeof(pin_buf));
2281 } else if (conference->tts_engine && conference->tts_voice) {
2282 pstatus =
2283 switch_ivr_speak_text(session, conference->tts_engine, conference->tts_voice, "please enter the conference pin number", NULL);
2284 } else {
2285 pstatus = switch_ivr_speak_text(session, "flite", "slt", "please enter the conference pin number", NULL);
2286 }
2287
2288 if (pstatus != SWITCH_STATUS_SUCCESS && pstatus != SWITCH_STATUS_BREAK) {
2289 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Cannot ask the user for a pin, ending call\n");
2290 switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
2291 }
2292
2293 /* wait for them if neccessary */
2294 if ((int)strlen(pin_buf) < maxpin) {
2295 char *buf = pin_buf + strlen(pin_buf);
2296 char term = '\0';
2297
2298 status = switch_ivr_collect_digits_count(session,
2299 buf,
2300 sizeof(pin_buf) - strlen(pin_buf), maxpin - strlen(pin_buf), "#", &term, 10000, 0, 0);
2301 if (status == SWITCH_STATUS_TIMEOUT) {
2302 status = SWITCH_STATUS_SUCCESS;
2303 }
2304 }
2305
2306 if (status == SWITCH_STATUS_SUCCESS) {
2307 validate_pin(pin_buf, dpin, mdpin);
2308 }
2309
2310 if (!pin_valid) {
2311 /* zero the collected pin */
2312 memset(pin_buf, 0, sizeof(pin_buf));
2313
2314 /* more friendliness */
2315 if (conference->bad_pin_sound) {
2316 conference_file_local_play(conference, session, conference->bad_pin_sound, 20, NULL, 0);
2317 }
2318 switch_channel_flush_dtmf(channel);
2319 }
2320 pin_retries--;
2321 }
2322
2323 if (!pin_valid) {
2324 conference_cdr_rejected(conference, channel, CDRR_PIN);
2325 goto done;
2326 }
2327 }
2328
2329 if (conference->special_announce && !switch_channel_test_app_flag_key("conference_silent", channel, CONF_SILENT_REQ)) {
2330 conference_file_local_play(conference, session, conference->special_announce, CONF_DEFAULT_LEADIN, NULL, 0);
2331 }
2332
2333 /* don't allow more callers if the conference is locked, unless we invited them */
2334 if (conference_utils_test_flag(conference, CFLAG_LOCKED) && enforce_security) {
2335 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Conference %s is locked.\n", conference_name);
2336 conference_cdr_rejected(conference, channel, CDRR_LOCKED);
2337 if (conference->locked_sound) {
2338 /* Answer the channel */
2339 switch_channel_answer(channel);
2340 conference_file_local_play(conference, session, conference->locked_sound, 20, NULL, 0);
2341 }
2342 goto done;
2343 }
2344
2345 /* dont allow more callers than the max_members allows for -- I explicitly didnt allow outbound calls
2346 * someone else can add that (see above) if they feel that outbound calls should be able to violate the
2347 * max_members limit
2348 */
2349 if ((conference->max_members > 0) && (conference->count >= conference->max_members)) {
2350 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_NOTICE, "Conference %s is full.\n", conference_name);
2351 conference_cdr_rejected(conference, channel, CDRR_MAXMEMBERS);
2352 if (conference->maxmember_sound) {
2353 /* Answer the channel */
2354 switch_channel_answer(channel);
2355 conference_file_local_play(conference, session, conference->maxmember_sound, 20, NULL, 0);
2356 }
2357 goto done;
2358 }
2359
2360 if (conference->member_enter_sound && !switch_channel_test_app_flag_key("conference_silent", channel, CONF_SILENT_REQ)) {
2361 conference_file_local_play(conference, session, conference->member_enter_sound, CONF_DEFAULT_LEADIN, NULL, 0);
2362 }
2363
2364 }
2365
2366 /* Release the config registry handle */
2367 switch_xml_free(cxml);
2368 cxml = NULL;
2369
2370 /* if we're using "bridge:" make an outbound call and bridge it in */
2371 if (!zstr(bridgeto) && strcasecmp(bridgeto, "none")) {
2372 switch_call_cause_t cause;
2373 if (conference_outcall(conference, NULL, session, bridgeto, 60, NULL, NULL, NULL, NULL, &cause, NULL, NULL, NULL) != SWITCH_STATUS_SUCCESS) {
2374 goto done;
2375 }
2376 } else {
2377 /* if we're not using "bridge:" set the conference answered flag */
2378 /* and this isn't an outbound channel, answer the call */
2379 if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND)
2380 conference_utils_set_flag(conference, CFLAG_ANSWERED);
2381 }
2382
2383 member.session = session;
2384 member.channel = switch_core_session_get_channel(session);
2385 member.pool = switch_core_session_get_pool(session);
2386
2387 /* Prepare MUTEXS */
2388 switch_mutex_init(&member.flag_mutex, SWITCH_MUTEX_NESTED, member.pool);
2389 switch_mutex_init(&member.write_mutex, SWITCH_MUTEX_NESTED, member.pool);
2390 switch_mutex_init(&member.read_mutex, SWITCH_MUTEX_NESTED, member.pool);
2391 switch_mutex_init(&member.fnode_mutex, SWITCH_MUTEX_NESTED, member.pool);
2392 switch_mutex_init(&member.audio_in_mutex, SWITCH_MUTEX_NESTED, member.pool);
2393 switch_mutex_init(&member.audio_out_mutex, SWITCH_MUTEX_NESTED, member.pool);
2394 switch_mutex_init(&member.text_mutex, SWITCH_MUTEX_NESTED, member.pool);
2395 switch_thread_rwlock_create(&member.rwlock, member.pool);
2396
2397 if (conference_member_setup_media(&member, conference)) {
2398 //flags = 0;
2399 goto done;
2400 }
2401
2402
2403 if (!(mid = switch_channel_get_private(channel, "__confmid"))) {
2404 mid = switch_core_session_alloc(session, sizeof(*mid));
2405 *mid = next_member_id();
2406 switch_channel_set_private(channel, "__confmid", mid);
2407 }
2408
2409 switch_channel_set_variable_printf(channel, "conference_member_id", "%u", *mid);
2410 member.id = *mid;
2411
2412
2413 /* Install our Signed Linear codec so we get the audio in that format */
2414 switch_core_session_set_read_codec(member.session, &member.read_codec);
2415
2416
2417 memcpy(mflags, conference->mflags, sizeof(mflags));
2418
2419 conference_utils_set_mflags(flags_str, mflags);
2420 mflags[MFLAG_RUNNING] = 1;
2421
2422 if (!(mflags[MFLAG_CAN_SPEAK])) {
2423 if (!(mflags[MFLAG_MUTE_DETECT])) {
2424 switch_core_media_hard_mute(member.session, SWITCH_TRUE);
2425 }
2426 }
2427
2428 if (!mflags[MFLAG_CAN_BE_SEEN]) {
2429 switch_channel_set_flag(channel, CF_VIDEO_PAUSE_READ);
2430 }
2431
2432 if (mpin_matched) {
2433 mflags[MFLAG_MOD] = 1;
2434 }
2435
2436 conference_utils_merge_mflags(member.flags, mflags);
2437
2438
2439 if (mflags[MFLAG_MINTWO]) {
2440 conference->min = 2;
2441 }
2442
2443
2444 if (conference->conference_video_mode == CONF_VIDEO_MODE_MUX) {
2445 switch_queue_create(&member.video_queue, 200, member.pool);
2446 switch_frame_buffer_create(&member.fb, 500);
2447 }
2448
2449 /* Add the caller to the conference */
2450 if (conference_member_add(conference, &member) != SWITCH_STATUS_SUCCESS) {
2451 switch_core_codec_destroy(&member.read_codec);
2452 goto done;
2453 }
2454
2455 if (conference->conference_video_mode == CONF_VIDEO_MODE_MUX) {
2456 conference_video_launch_muxing_write_thread(&member);
2457 conference_video_launch_layer_thread(&member);
2458 }
2459
2460 msg.from = __FILE__;
2461
2462 /* Tell the channel we are going to be in a bridge */
2463 msg.message_id = SWITCH_MESSAGE_INDICATE_BRIDGE;
2464 switch_core_session_receive_message(session, &msg);
2465
2466 if (conference_utils_test_flag(conference, CFLAG_TRANSCODE_VIDEO)) {
2467 switch_channel_set_flag_recursive(channel, CF_VIDEO_DECODED_READ);
2468 switch_core_media_gen_key_frame(session);
2469 }
2470
2471 /* Chime in the core video thread */
2472 switch_core_session_set_video_read_callback(session, conference_video_thread_callback, (void *)&member);
2473 switch_core_session_set_text_read_callback(session, conference_text_thread_callback, (void *)&member);
2474
2475 if (switch_channel_test_flag(channel, CF_VIDEO_ONLY) || !switch_channel_test_flag(channel, CF_AUDIO)) {
2476 while(conference_utils_member_test_flag((&member), MFLAG_RUNNING) && switch_channel_ready(channel)) {
2477 switch_frame_t *read_frame;
2478 if (switch_channel_test_flag(channel, CF_AUDIO)) {
2479 switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0);
2480 }
2481 switch_yield(100000);
2482 }
2483 } else {
2484
2485 /* Run the conference loop */
2486 do {
2487 conference_loop_output(&member);
2488 } while (member.loop_loop);
2489 }
2490
2491 switch_core_session_video_reset(session);
2492 switch_channel_clear_flag_recursive(channel, CF_VIDEO_DECODED_READ);
2493
2494 switch_core_session_set_video_read_callback(session, NULL, NULL);
2495 switch_core_session_set_text_read_callback(session, NULL, NULL);
2496
2497 switch_channel_set_private(channel, "_conference_autocall_list_", NULL);
2498
2499 /* Tell the channel we are no longer going to be in a bridge */
2500 msg.message_id = SWITCH_MESSAGE_INDICATE_UNBRIDGE;
2501 switch_core_session_receive_message(session, &msg);
2502
2503 conference_utils_member_clear_flag(&member, MFLAG_RUNNING);
2504
2505 if (member.video_muxing_write_thread) {
2506 switch_status_t st = SWITCH_STATUS_SUCCESS;
2507 switch_frame_buffer_push(member.fb, NULL);
2508 switch_thread_join(&st, member.video_muxing_write_thread);
2509 member.video_muxing_write_thread = NULL;
2510 }
2511
2512 if (member.video_layer_thread) {
2513 switch_status_t st = SWITCH_STATUS_SUCCESS;
2514
2515 while(member.layer_thread_running) {
2516 conference_video_wake_layer_thread(&member);
2517 switch_yield(10000);
2518 }
2519
2520 switch_thread_join(&st, member.video_layer_thread);
2521 member.video_layer_thread = NULL;
2522 }
2523
2524 /* Remove the caller from the conference */
2525 conference_member_del(member.conference, &member);
2526
2527 /* Put the original codec back */
2528 switch_core_session_set_read_codec(member.session, NULL);
2529
2530 /* Clean Up. */
2531
2532 done:
2533
2534 if (locked) {
2535 switch_mutex_unlock(conference_globals.setup_mutex);
2536 }
2537
2538 if (member.read_resampler) {
2539 switch_resample_destroy(&member.read_resampler);
2540 }
2541
2542 switch_event_destroy(¶ms);
2543 switch_buffer_destroy(&member.resample_buffer);
2544 switch_buffer_destroy(&member.audio_buffer);
2545 switch_buffer_destroy(&member.mux_buffer);
2546
2547 if (member.fb) {
2548 switch_frame_buffer_destroy(&member.fb);
2549 }
2550
2551 if (conference) {
2552 switch_mutex_lock(conference->mutex);
2553 if (conference_utils_test_flag(conference, CFLAG_DYNAMIC) && conference->count == 0 && conference->count_ghosts == 0) {
2554 conference_utils_set_flag_locked(conference, CFLAG_DESTRUCT);
2555 }
2556 switch_mutex_unlock(conference->mutex);
2557 }
2558
2559 /* Release the config registry handle */
2560 if (cxml) {
2561 switch_xml_free(cxml);
2562 }
2563
2564 if (conference && conference_utils_member_test_flag(&member, MFLAG_KICKED) && conference->kicked_sound) {
2565 char *toplay = NULL;
2566 char *dfile = NULL;
2567 char *expanded = NULL;
2568 char *src = member.kicked_sound ? member.kicked_sound : conference->kicked_sound;
2569
2570
2571 if (!strncasecmp(src, "say:", 4)) {
2572 if (conference->tts_engine && conference->tts_voice) {
2573 switch_ivr_speak_text(session, conference->tts_engine, conference->tts_voice, src + 4, NULL);
2574 }
2575 } else {
2576 if ((expanded = switch_channel_expand_variables(switch_core_session_get_channel(session), src)) != src) {
2577 toplay = expanded;
2578 } else {
2579 expanded = NULL;
2580 toplay = src;
2581 }
2582
2583 if (!switch_is_file_path(toplay) && conference->sound_prefix) {
2584 dfile = switch_mprintf("%s%s%s", conference->sound_prefix, SWITCH_PATH_SEPARATOR, toplay);
2585 switch_assert(dfile);
2586 toplay = dfile;
2587 }
2588
2589 switch_ivr_play_file(session, NULL, toplay, NULL);
2590 switch_safe_free(dfile);
2591 switch_safe_free(expanded);
2592 }
2593 }
2594
2595 switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE);
2596
2597 /* release the readlock */
2598 if (rl) {
2599 switch_thread_rwlock_unlock(conference->rwlock);
2600 }
2601
2602 switch_channel_set_variable(channel, "last_transfered_conference", NULL);
2603
2604 end:
2605
2606 switch_channel_clear_flag(channel, CF_CONFERENCE);
2607
2608 switch_core_session_video_reset(session);
2609 }
2610
2611
2612
2613 /* Create a thread for the conference and launch it */
conference_launch_thread(conference_obj_t * conference)2614 void conference_launch_thread(conference_obj_t *conference)
2615 {
2616 switch_thread_t *thread;
2617 switch_threadattr_t *thd_attr = NULL;
2618
2619 conference_utils_set_flag_locked(conference, CFLAG_RUNNING);
2620 switch_threadattr_create(&thd_attr, conference->pool);
2621 switch_threadattr_detach_set(thd_attr, 1);
2622 switch_threadattr_priority_set(thd_attr, SWITCH_PRI_REALTIME);
2623 switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
2624 switch_mutex_lock(conference_globals.hash_mutex);
2625 switch_mutex_unlock(conference_globals.hash_mutex);
2626 switch_thread_create(&thread, thd_attr, conference_thread_run, conference, conference->pool);
2627 }
2628
conference_find(char * name,char * domain)2629 conference_obj_t *conference_find(char *name, char *domain)
2630 {
2631 conference_obj_t *conference;
2632
2633 switch_mutex_lock(conference_globals.hash_mutex);
2634 if ((conference = switch_core_hash_find(conference_globals.conference_hash, name))) {
2635 if (conference_utils_test_flag(conference, CFLAG_DESTRUCT)) {
2636 switch_core_hash_delete(conference_globals.conference_hash, conference->name);
2637 conference_utils_clear_flag(conference, CFLAG_INHASH);
2638 conference = NULL;
2639 } else if (!zstr(domain) && conference->domain && strcasecmp(domain, conference->domain)) {
2640 conference = NULL;
2641 }
2642 }
2643 if (conference) {
2644 if (switch_thread_rwlock_tryrdlock(conference->rwlock) != SWITCH_STATUS_SUCCESS) {
2645 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Read Lock Fail\n");
2646 conference = NULL;
2647 }
2648 }
2649 switch_mutex_unlock(conference_globals.hash_mutex);
2650
2651 return conference;
2652 }
2653
conference_set_variable(conference_obj_t * conference,const char * var,const char * val)2654 void conference_set_variable(conference_obj_t *conference, const char *var, const char *val)
2655 {
2656 switch_mutex_lock(conference->flag_mutex);
2657 switch_event_add_header_string(conference->variables, SWITCH_STACK_BOTTOM, var, val);
2658 switch_mutex_unlock(conference->flag_mutex);
2659 }
2660
conference_get_variable(conference_obj_t * conference,const char * var)2661 const char *conference_get_variable(conference_obj_t *conference, const char *var)
2662 {
2663 const char *val;
2664
2665 switch_mutex_lock(conference->flag_mutex);
2666 val = switch_event_get_header(conference->variables, var);
2667 switch_mutex_unlock(conference->flag_mutex);
2668
2669 if (val) {
2670 return switch_core_strdup(conference->pool, val);
2671 }
2672
2673 return NULL;
2674 }
2675
2676 /* create a new conferene with a specific profile */
conference_new(char * name,conference_xml_cfg_t cfg,switch_core_session_t * session,switch_memory_pool_t * pool)2677 conference_obj_t *conference_new(char *name, conference_xml_cfg_t cfg, switch_core_session_t *session, switch_memory_pool_t *pool)
2678 {
2679 conference_obj_t *conference;
2680 switch_xml_t xml_kvp;
2681 char *timer_name = NULL;
2682 char *domain = NULL;
2683 char *desc = NULL;
2684 char *name_domain = NULL;
2685 char *tts_engine = NULL;
2686 char *tts_voice = NULL;
2687 char *member_enter_sound = NULL;
2688 char *enter_sound = NULL;
2689 char *sound_prefix = NULL;
2690 char *exit_sound = NULL;
2691 char *alone_sound = NULL;
2692 char *muted_sound = NULL;
2693 char *mute_detect_sound = NULL;
2694 char *unmuted_sound = NULL;
2695 char *locked_sound = NULL;
2696 char *is_locked_sound = NULL;
2697 char *is_unlocked_sound = NULL;
2698 char *kicked_sound = NULL;
2699 char *deaf_sound = NULL;
2700 char *undeaf_sound = NULL;
2701 char *blind_sound = NULL;
2702 char *unblind_sound = NULL;
2703 char *pin = NULL;
2704 char *mpin = NULL;
2705 char *pin_sound = NULL;
2706 char *bad_pin_sound = NULL;
2707 char *energy_level = NULL;
2708 char *auto_energy_level = NULL;
2709 char *max_energy_level = NULL;
2710 char *max_energy_hit_trigger = NULL;
2711 char *max_energy_level_mute_ms = NULL;
2712 char *auto_energy_sec = NULL;
2713 char *agc_level = NULL;
2714 char *agc_low_energy_level = NULL;
2715 char *agc_margin = NULL;
2716 char *agc_change_factor = NULL;
2717 char *agc_period_len = NULL;
2718 char *caller_id_name = NULL;
2719 char *caller_id_number = NULL;
2720 char *caller_controls = NULL;
2721 char *moderator_controls = NULL;
2722 char *member_flags = NULL;
2723 char *conference_flags = NULL;
2724 char *perpetual_sound = NULL;
2725 char *moh_sound = NULL;
2726 char *outcall_templ = NULL;
2727 char *video_layout_conf = NULL;
2728 char *video_layout_name = NULL;
2729 char *video_layout_group = NULL;
2730 char *video_canvas_size = NULL;
2731 char *video_canvas_bgcolor = NULL;
2732 char *video_border_color = NULL;
2733 int video_border_size = 0;
2734 char *video_super_canvas_bgcolor = NULL;
2735 char *video_letterbox_bgcolor = NULL;
2736 char *video_codec_bandwidth = NULL;
2737 char *no_video_avatar = NULL;
2738 char *video_mute_banner = NULL;
2739 conference_video_mode_t conference_video_mode = CONF_VIDEO_MODE_PASSTHROUGH;
2740 int conference_video_quality = 1;
2741 int auto_kps_debounce = 5000;
2742 float fps = 30.0f;
2743 uint32_t max_members = 0;
2744 uint32_t announce_count = 0;
2745 char *maxmember_sound = NULL;
2746 uint32_t rate = 8000, interval = 20;
2747 uint32_t channels = 1;
2748 int broadcast_chat_messages = 1;
2749 int comfort_noise_level = 0;
2750 int pin_retries = 3;
2751 int ivr_dtmf_timeout = 500;
2752 int ivr_input_timeout = 0;
2753 int video_canvas_count = 0;
2754 int video_super_canvas_label_layers = 0;
2755 int video_super_canvas_show_all_layers = 0;
2756 char *suppress_events = NULL;
2757 char *verbose_events = NULL;
2758 char *auto_record = NULL;
2759 char *recording_metadata = NULL;
2760 int min_recording_participants = 1;
2761 char *conference_log_dir = NULL;
2762 char *cdr_event_mode = NULL;
2763 char *terminate_on_silence = NULL;
2764 char *endconference_grace_time = NULL;
2765 char uuid_str[SWITCH_UUID_FORMATTED_LENGTH+1];
2766 switch_uuid_t uuid;
2767 switch_codec_implementation_t read_impl = { 0 };
2768 switch_channel_t *channel = NULL;
2769 const char *force_rate = NULL, *force_interval = NULL, *force_channels = NULL, *presence_id = NULL, *force_canvas_size = NULL;
2770 uint32_t force_rate_i = 0, force_interval_i = 0, force_channels_i = 0, video_auto_floor_msec = 0;
2771 switch_event_t *event;
2772
2773 int scale_h264_canvas_width = 0;
2774 int scale_h264_canvas_height = 0;
2775 int scale_h264_canvas_fps_divisor = 0;
2776 char *scale_h264_canvas_bandwidth = NULL;
2777 char *video_codec_config_profile_name = NULL;
2778 int tmp;
2779
2780 /* Validate the conference name */
2781 if (zstr(name)) {
2782 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Record! no name.\n");
2783 return NULL;
2784 }
2785
2786 if (session) {
2787 uint32_t tmp;
2788
2789 switch_core_session_get_read_impl(session, &read_impl);
2790 channel = switch_core_session_get_channel(session);
2791
2792 presence_id = switch_channel_get_variable(channel, "presence_id");
2793
2794 if ((force_rate = switch_channel_get_variable(channel, "conference_force_rate"))) {
2795 if (!strcasecmp(force_rate, "auto")) {
2796 force_rate_i = rate = read_impl.actual_samples_per_second;
2797 } else {
2798 tmp = atoi(force_rate);
2799
2800 if (tmp == 8000 || tmp == 12000 || tmp == 16000 || tmp == 24000 || tmp == 32000 || tmp == 44100 || tmp == 48000) {
2801 force_rate_i = rate = tmp;
2802 }
2803 }
2804 }
2805
2806 if ((force_channels = switch_channel_get_variable(channel, "conference_force_channels"))) {
2807 if (!strcasecmp(force_channels, "auto")) {
2808 force_channels_i = channels = read_impl.number_of_channels;
2809 } else {
2810 tmp = atoi(force_channels);
2811
2812 if (tmp == 1 || tmp == 2) {
2813 force_channels_i = channels = tmp;
2814 }
2815 }
2816 }
2817
2818 if ((force_interval = switch_channel_get_variable(channel, "conference_force_interval"))) {
2819 if (!strcasecmp(force_interval, "auto")) {
2820 force_interval_i = interval = read_impl.microseconds_per_packet / 1000;
2821 } else {
2822 tmp = atoi(force_interval);
2823
2824 if (SWITCH_ACCEPTABLE_INTERVAL(tmp)) {
2825 force_interval_i = interval = tmp;
2826 }
2827 }
2828 }
2829
2830
2831 if ((force_canvas_size = switch_channel_get_variable(channel, "conference_force_canvas_size"))) {
2832 video_canvas_size = (char *)force_canvas_size;
2833 }
2834 }
2835
2836 switch_mutex_lock(conference_globals.hash_mutex);
2837
2838 /* parse the profile tree for param values */
2839 if (cfg.profile)
2840 for (xml_kvp = switch_xml_child(cfg.profile, "param"); xml_kvp; xml_kvp = xml_kvp->next) {
2841 char *var = (char *) switch_xml_attr_soft(xml_kvp, "name");
2842 char *val = (char *) switch_xml_attr_soft(xml_kvp, "value");
2843 char buf[128] = "";
2844 char *p;
2845
2846 if (strchr(var, '_')) {
2847 switch_copy_string(buf, var, sizeof(buf));
2848 for (p = buf; *p; p++) {
2849 if (*p == '_') {
2850 *p = '-';
2851 }
2852 }
2853 var = buf;
2854 }
2855
2856 if (!force_rate_i && !strcasecmp(var, "rate") && !zstr(val)) {
2857 uint32_t tmp = atoi(val);
2858 if (session && tmp == 0) {
2859 if (!strcasecmp(val, "auto")) {
2860 rate = read_impl.actual_samples_per_second;
2861 }
2862 } else {
2863 if (tmp == 8000 || tmp == 12000 || tmp == 16000 || tmp == 24000 || tmp == 32000 || tmp == 44100 || tmp == 48000) {
2864 rate = tmp;
2865 }
2866 }
2867 } else if (!force_channels_i && !strcasecmp(var, "channels") && !zstr(val)) {
2868 uint32_t tmp = atoi(val);
2869 if (session && tmp == 0) {
2870 if (!strcasecmp(val, "auto")) {
2871 channels = read_impl.number_of_channels;
2872 }
2873 } else {
2874 if (tmp == 1 || tmp == 2) {
2875 channels = tmp;
2876 }
2877 }
2878 } else if (!strcasecmp(var, "domain") && !zstr(val)) {
2879 domain = val;
2880 } else if (!strcasecmp(var, "description") && !zstr(val)) {
2881 desc = val;
2882 } else if (!force_interval_i && !strcasecmp(var, "interval") && !zstr(val)) {
2883 uint32_t tmp = atoi(val);
2884
2885 if (session && tmp == 0) {
2886 if (!strcasecmp(val, "auto")) {
2887 interval = read_impl.microseconds_per_packet / 1000;
2888 }
2889 } else {
2890 if (SWITCH_ACCEPTABLE_INTERVAL(tmp)) {
2891 interval = tmp;
2892 } else {
2893 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
2894 "Interval must be multipe of 10 and less than %d, Using default of 20\n", SWITCH_MAX_INTERVAL);
2895 }
2896 }
2897 } else if (!strcasecmp(var, "timer-name") && !zstr(val)) {
2898 timer_name = val;
2899 } else if (!strcasecmp(var, "tts-engine") && !zstr(val)) {
2900 tts_engine = val;
2901 } else if (!strcasecmp(var, "tts-voice") && !zstr(val)) {
2902 tts_voice = val;
2903 } else if (!strcasecmp(var, "member-enter-sound") && !zstr(val)) {
2904 member_enter_sound = val;
2905 } else if (!strcasecmp(var, "enter-sound") && !zstr(val)) {
2906 enter_sound = val;
2907 } else if (!strcasecmp(var, "outcall-templ") && !zstr(val)) {
2908 outcall_templ = val;
2909 } else if (!strcasecmp(var, "video-layout-name") && !zstr(val)) {
2910 video_layout_name = val;
2911 } else if (!strcasecmp(var, "video-layout-conf") && !zstr(val)) {
2912 video_layout_conf = val;
2913 } else if (!strcasecmp(var, "video-canvas-count") && !zstr(val)) {
2914 video_canvas_count = atoi(val);
2915 } else if (!strcasecmp(var, "video-super-canvas-label-layers") && !zstr(val)) {
2916 video_super_canvas_label_layers = atoi(val);
2917 } else if (!strcasecmp(var, "video-super-canvas-show-all-layers") && !zstr(val)) {
2918 video_super_canvas_show_all_layers = atoi(val);
2919 } else if (!strcasecmp(var, "video-canvas-bgcolor") && !zstr(val)) {
2920 video_canvas_bgcolor = val;
2921 } else if (!strcasecmp(var, "video-border-color") && !zstr(val)) {
2922 video_border_color = val;
2923 } else if (!strcasecmp(var, "video-border-size") && !zstr(val)) {
2924 video_border_size = atoi(val);
2925 } else if (!strcasecmp(var, "video-super-canvas-bgcolor") && !zstr(val)) {
2926 video_super_canvas_bgcolor= val;
2927 } else if (!strcasecmp(var, "video-letterbox-bgcolor") && !zstr(val)) {
2928 video_letterbox_bgcolor= val;
2929 } else if (!video_canvas_size && !strcasecmp(var, "video-canvas-size") && !zstr(val)) {
2930 video_canvas_size = val;
2931 } else if (!strcasecmp(var, "video-fps") && !zstr(val)) {
2932 fps = (float)atof(val);
2933 } else if (!strcasecmp(var, "video-codec-bandwidth") && !zstr(val)) {
2934 video_codec_bandwidth = val;
2935 } else if (!strcasecmp(var, "video-no-video-avatar") && !zstr(val)) {
2936 no_video_avatar = val;
2937 } else if (!strcasecmp(var, "video-mute-banner") && !zstr(val)) {
2938 video_mute_banner = val;
2939 } else if (!strcasecmp(var, "exit-sound") && !zstr(val)) {
2940 exit_sound = val;
2941 } else if (!strcasecmp(var, "alone-sound") && !zstr(val)) {
2942 alone_sound = val;
2943 } else if (!strcasecmp(var, "perpetual-sound") && !zstr(val)) {
2944 perpetual_sound = val;
2945 } else if (!strcasecmp(var, "moh-sound") && !zstr(val)) {
2946 moh_sound = val;
2947 } else if (!strcasecmp(var, "muted-sound") && !zstr(val)) {
2948 muted_sound = val;
2949 } else if (!strcasecmp(var, "mute-detect-sound") && !zstr(val)) {
2950 mute_detect_sound = val;
2951 } else if (!strcasecmp(var, "unmuted-sound") && !zstr(val)) {
2952 unmuted_sound = val;
2953 } else if (!strcasecmp(var, "locked-sound") && !zstr(val)) {
2954 locked_sound = val;
2955 } else if (!strcasecmp(var, "is-locked-sound") && !zstr(val)) {
2956 is_locked_sound = val;
2957 } else if (!strcasecmp(var, "is-unlocked-sound") && !zstr(val)) {
2958 is_unlocked_sound = val;
2959 } else if (!strcasecmp(var, "deaf-sound") && !zstr(val)) {
2960 deaf_sound = val;
2961 } else if (!strcasecmp(var, "undeaf-sound") && !zstr(val)) {
2962 undeaf_sound = val;
2963 } else if (!strcasecmp(var, "blind-sound") && !zstr(val)) {
2964 blind_sound = val;
2965 } else if (!strcasecmp(var, "unblind-sound") && !zstr(val)) {
2966 unblind_sound = val;
2967 } else if (!strcasecmp(var, "member-flags") && !zstr(val)) {
2968 member_flags = val;
2969 } else if (!strcasecmp(var, "conference-flags") && !zstr(val)) {
2970 conference_flags = val;
2971 } else if (!strcasecmp(var, "cdr-log-dir") && !zstr(val)) {
2972 conference_log_dir = val;
2973 } else if (!strcasecmp(var, "cdr-event-mode") && !zstr(val)) {
2974 cdr_event_mode = val;
2975 } else if (!strcasecmp(var, "kicked-sound") && !zstr(val)) {
2976 kicked_sound = val;
2977 } else if (!strcasecmp(var, "pin") && !zstr(val)) {
2978 pin = val;
2979 } else if (!strcasecmp(var, "moderator-pin") && !zstr(val)) {
2980 mpin = val;
2981 } else if (!strcasecmp(var, "pin-retries") && !zstr(val)) {
2982 int tmp = atoi(val);
2983 if (tmp >= 0) {
2984 pin_retries = tmp;
2985 }
2986 } else if (!strcasecmp(var, "pin-sound") && !zstr(val)) {
2987 pin_sound = val;
2988 } else if (!strcasecmp(var, "bad-pin-sound") && !zstr(val)) {
2989 bad_pin_sound = val;
2990 } else if (!strcasecmp(var, "energy-level") && !zstr(val)) {
2991 energy_level = val;
2992 } else if (!strcasecmp(var, "auto-energy-level") && !zstr(val)) {
2993 auto_energy_level = val;
2994 } else if (!strcasecmp(var, "max-energy-level") && !zstr(val)) {
2995 max_energy_level = val;
2996 } else if (!strcasecmp(var, "max-energy-hit-trigger") && !zstr(val)) {
2997 max_energy_hit_trigger = val;
2998 } else if (!strcasecmp(var, "max-energy-level-mute-ms") && !zstr(val)) {
2999 max_energy_level_mute_ms = val;
3000 } else if (!strcasecmp(var, "auto-energy-seconds") && !zstr(val)) {
3001 auto_energy_sec = val;
3002 } else if (!strcasecmp(var, "auto-gain-level") && !zstr(val)) {
3003 agc_level = val;
3004 } else if (!strcasecmp(var, "auto-gain-low-energy-level") && !zstr(val)) {
3005 agc_low_energy_level = val;
3006 } else if (!strcasecmp(var, "auto-gain-margin") && !zstr(val)) {
3007 agc_margin = val;
3008 } else if (!strcasecmp(var, "auto-gain-change-factor") && !zstr(val)) {
3009 agc_change_factor = val;
3010 } else if (!strcasecmp(var, "auto-gain-period-len") && !zstr(val)) {
3011 agc_period_len = val;
3012 } else if (!strcasecmp(var, "caller-id-name") && !zstr(val)) {
3013 caller_id_name = val;
3014 } else if (!strcasecmp(var, "caller-id-number") && !zstr(val)) {
3015 caller_id_number = val;
3016 } else if (!strcasecmp(var, "caller-controls") && !zstr(val)) {
3017 caller_controls = val;
3018 } else if (!strcasecmp(var, "ivr-dtmf-timeout") && !zstr(val)) {
3019 ivr_dtmf_timeout = atoi(val);
3020 if (ivr_dtmf_timeout < 500) {
3021 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "not very smart value for ivr-dtmf-timeout found (%d), defaulting to 500ms\n", ivr_dtmf_timeout);
3022 ivr_dtmf_timeout = 500;
3023 }
3024 } else if (!strcasecmp(var, "ivr-input-timeout") && !zstr(val)) {
3025 ivr_input_timeout = atoi(val);
3026 if (ivr_input_timeout != 0 && ivr_input_timeout < 500) {
3027 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "not very smart value for ivr-input-timeout found (%d), defaulting to 500ms\n", ivr_input_timeout);
3028 ivr_input_timeout = 5000;
3029 }
3030 } else if (!strcasecmp(var, "moderator-controls") && !zstr(val)) {
3031 moderator_controls = val;
3032 } else if (!strcasecmp(var, "broadcast-chat-messages") && !zstr(val)) {
3033 broadcast_chat_messages = switch_true(val);
3034 } else if (!strcasecmp(var, "comfort-noise") && !zstr(val)) {
3035 int tmp;
3036 tmp = atoi(val);
3037 if (tmp > 1 && tmp < 10000) {
3038 comfort_noise_level = tmp;
3039 } else if (switch_true(val)) {
3040 comfort_noise_level = 1400;
3041 }
3042 } else if (!strcasecmp(var, "video-auto-floor-msec") && !zstr(val)) {
3043 int tmp;
3044 tmp = atoi(val);
3045
3046 if (tmp > 0) {
3047 video_auto_floor_msec = tmp;
3048 }
3049 } else if (!strcasecmp(var, "sound-prefix") && !zstr(val)) {
3050 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "override sound-prefix with: %s\n", val);
3051 sound_prefix = val;
3052 } else if (!strcasecmp(var, "max-members") && !zstr(val)) {
3053 errno = 0; /* sanity first */
3054 max_members = strtol(val, NULL, 0); /* base 0 lets 0x... for hex 0... for octal and base 10 otherwise through */
3055 if (errno == ERANGE || errno == EINVAL || (int32_t) max_members < 0 || max_members == 1) {
3056 /* a negative wont work well, and its foolish to have a conference limited to 1 person unless the outbound
3057 * stuff is added, see comments above
3058 */
3059 max_members = 0; /* set to 0 to disable max counts */
3060 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "max-members %s is invalid, not setting a limit\n", val);
3061 }
3062 } else if (!strcasecmp(var, "max-members-sound") && !zstr(val)) {
3063 maxmember_sound = val;
3064 } else if (!strcasecmp(var, "announce-count") && !zstr(val)) {
3065 errno = 0; /* safety first */
3066 announce_count = strtol(val, NULL, 0);
3067 if (errno == ERANGE || errno == EINVAL) {
3068 announce_count = 0;
3069 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "announce-count is invalid, not anouncing member counts\n");
3070 }
3071 } else if (!strcasecmp(var, "suppress-events") && !zstr(val)) {
3072 suppress_events = val;
3073 } else if (!strcasecmp(var, "verbose-events") && !zstr(val)) {
3074 verbose_events = val;
3075 } else if (!strcasecmp(var, "auto-record") && !zstr(val)) {
3076 auto_record = val;
3077 } else if (!strcasecmp(var, "recording-metadata") && !zstr(val)) {
3078 recording_metadata = val;
3079 } else if (!strcasecmp(var, "min-required-recording-participants") && !zstr(val)) {
3080 if (!strcmp(val, "1")) {
3081 min_recording_participants = 1;
3082 } else if (!strcmp(val, "2")) {
3083 min_recording_participants = 2;
3084 } else {
3085 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "min-required-recording-participants is invalid, leaving set to %d\n", min_recording_participants);
3086 }
3087 } else if (!strcasecmp(var, "terminate-on-silence") && !zstr(val)) {
3088 terminate_on_silence = val;
3089 } else if (!strcasecmp(var, "endconf-grace-time") && !zstr(val)) {
3090 endconference_grace_time = val;
3091 } else if (!strcasecmp(var, "video-quality") && !zstr(val)) {
3092 int tmp = atoi(val);
3093
3094 if (tmp > -1 && tmp < 5) {
3095 conference_video_quality = tmp;
3096 } else {
3097 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Video quality must be between 0 and 4\n");
3098 }
3099 } else if (!strcasecmp(var, "video-kps-debounce") && !zstr(val)) {
3100 int tmp = atoi(val);
3101
3102 if (tmp >= 0) {
3103 auto_kps_debounce = tmp;
3104 } else {
3105 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "video-kps-debounce must be 0 or higher\n");
3106 }
3107
3108 } else if (!strcasecmp(var, "video-mode") && !zstr(val)) {
3109 if (!strcasecmp(val, "passthrough")) {
3110 conference_video_mode = CONF_VIDEO_MODE_PASSTHROUGH;
3111 } else if (!strcasecmp(val, "transcode")) {
3112 conference_video_mode = CONF_VIDEO_MODE_TRANSCODE;
3113 } else if (!strcasecmp(val, "mux")) {
3114 conference_video_mode = CONF_VIDEO_MODE_MUX;
3115 } else {
3116 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "video-mode invalid, valid settings are 'passthrough', 'transcode' and 'mux'\n");
3117 }
3118 } else if (!strcasecmp(var, "scale-h264-canvas-size") && !zstr(val)) {
3119 char *p;
3120
3121 if ((scale_h264_canvas_width = atoi(val))) {
3122 if ((p = strchr(val, 'x'))) {
3123 p++;
3124 if (*p) {
3125 scale_h264_canvas_height = atoi(p);
3126 }
3127 }
3128 }
3129
3130 if (scale_h264_canvas_width < 320 || scale_h264_canvas_height < 180) {
3131 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid scale-h264-canvas-size, falling back to 320x180\n");
3132 scale_h264_canvas_width = 320;
3133 scale_h264_canvas_height = 180;
3134 }
3135 } else if (!strcasecmp(var, "scale-h264-canvas-fps-divisor") && !zstr(val)) {
3136 scale_h264_canvas_fps_divisor = atoi(val);
3137 if (scale_h264_canvas_fps_divisor < 0) scale_h264_canvas_fps_divisor = 0;
3138 } else if (!strcasecmp(var, "scale-h264-canvas-bandwidth") && !zstr(val)) {
3139 scale_h264_canvas_bandwidth = val;
3140 } else if (!strcasecmp(var, "video-codec-config-profile-name") && !zstr(val)) {
3141 video_codec_config_profile_name = val;
3142 }
3143 }
3144
3145 /* Set defaults and various paramaters */
3146
3147 if (zstr(video_layout_conf)) {
3148 video_layout_conf = "conference_layouts.conf";
3149 }
3150
3151 /* Timer module to use */
3152 if (zstr(timer_name)) {
3153 timer_name = "soft";
3154 }
3155
3156 /* Caller ID Name */
3157 if (zstr(caller_id_name)) {
3158 caller_id_name = (char *) mod_conference_app_name;
3159 }
3160
3161 /* Caller ID Number */
3162 if (zstr(caller_id_number)) {
3163 caller_id_number = SWITCH_DEFAULT_CLID_NUMBER;
3164 }
3165
3166 if (!pool) {
3167 /* Setup a memory pool to use. */
3168 if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
3169 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Pool Failure\n");
3170 conference = NULL;
3171 goto end;
3172 }
3173 }
3174
3175 /* Create the conference object. */
3176 if (!(conference = switch_core_alloc(pool, sizeof(*conference)))) {
3177 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
3178 conference = NULL;
3179 goto end;
3180 }
3181
3182 conference->start_time = switch_epoch_time_now(NULL);
3183
3184 /* initialize the conference object with settings from the specified profile */
3185 conference->pool = pool;
3186 conference->profile_name = switch_core_strdup(conference->pool, cfg.profile ? switch_xml_attr_soft(cfg.profile, "name") : "none");
3187 if (timer_name) {
3188 conference->timer_name = switch_core_strdup(conference->pool, timer_name);
3189 }
3190 if (tts_engine) {
3191 conference->tts_engine = switch_core_strdup(conference->pool, tts_engine);
3192 }
3193 if (tts_voice) {
3194 conference->tts_voice = switch_core_strdup(conference->pool, tts_voice);
3195 }
3196
3197 conference->video_layout_conf = switch_core_strdup(conference->pool, video_layout_conf);
3198 conference->comfort_noise_level = comfort_noise_level;
3199 conference->pin_retries = pin_retries;
3200 conference->caller_id_name = switch_core_strdup(conference->pool, caller_id_name);
3201 conference->caller_id_number = switch_core_strdup(conference->pool, caller_id_number);
3202 conference->caller_controls = switch_core_strdup(conference->pool, caller_controls);
3203 conference->moderator_controls = switch_core_strdup(conference->pool, moderator_controls);
3204 conference->broadcast_chat_messages = broadcast_chat_messages;
3205 conference->video_quality = conference_video_quality;
3206 conference->auto_kps_debounce = auto_kps_debounce;
3207 switch_event_create_plain(&conference->variables, SWITCH_EVENT_CHANNEL_DATA);
3208 conference->conference_video_mode = conference_video_mode;
3209 conference->video_codec_config_profile_name = switch_core_strdup(conference->pool, video_codec_config_profile_name);
3210
3211 conference->scale_h264_canvas_width = scale_h264_canvas_width;
3212 conference->scale_h264_canvas_height = scale_h264_canvas_height;
3213 conference->scale_h264_canvas_fps_divisor = scale_h264_canvas_fps_divisor;
3214 conference->scale_h264_canvas_bandwidth = switch_core_strdup(conference->pool, scale_h264_canvas_bandwidth);
3215
3216 if (!switch_core_has_video() && (conference->conference_video_mode == CONF_VIDEO_MODE_MUX || conference->conference_video_mode == CONF_VIDEO_MODE_TRANSCODE)) {
3217 conference->conference_video_mode = CONF_VIDEO_MODE_PASSTHROUGH;
3218 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "video-mode invalid, only valid setting is 'passthrough' due to no video capabilities\n");
3219 }
3220
3221 if (conference->conference_video_mode == CONF_VIDEO_MODE_MUX) {
3222 int canvas_w = 0, canvas_h = 0;
3223 if (video_canvas_size) {
3224 char *p;
3225
3226 if ((canvas_w = atoi(video_canvas_size))) {
3227 if ((p = strchr(video_canvas_size, 'x'))) {
3228 p++;
3229 if (*p) {
3230 canvas_h = atoi(p);
3231 }
3232 }
3233 }
3234 }
3235
3236 if ((canvas_w % 2) != 0) {
3237 canvas_w++;
3238 }
3239
3240 if ((canvas_h % 2) != 0) {
3241 canvas_h++;
3242 }
3243
3244 if (canvas_w < 320 || canvas_h < 180) {
3245 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "%s video-canvas-size, falling back to %ux%u\n",
3246 video_canvas_size ? "Invalid" : "Unspecified", CONFERENCE_CANVAS_DEFAULT_WIDTH, CONFERENCE_CANVAS_DEFAULT_HIGHT);
3247 canvas_w = CONFERENCE_CANVAS_DEFAULT_WIDTH;
3248 canvas_h = CONFERENCE_CANVAS_DEFAULT_HIGHT;
3249 }
3250
3251 if (video_border_size) {
3252 if (video_border_size < 0) video_border_size = 0;
3253 if (video_border_size > 50) video_border_size = 50;
3254 }
3255 conference->video_border_size = video_border_size;
3256
3257 conference_video_parse_layouts(conference, canvas_w, canvas_h);
3258
3259 if (!video_canvas_bgcolor) {
3260 video_canvas_bgcolor = "#333333";
3261 }
3262
3263 if (!video_border_color) {
3264 video_border_color = "#00ff00";
3265 }
3266
3267 if (!video_super_canvas_bgcolor) {
3268 video_super_canvas_bgcolor = "#068df3";
3269 }
3270
3271 if (!video_letterbox_bgcolor) {
3272 video_letterbox_bgcolor = "#000000";
3273 }
3274
3275 if (video_mute_banner) {
3276 conference->video_mute_banner = switch_core_strdup(conference->pool, video_mute_banner);
3277 }
3278
3279 if (no_video_avatar) {
3280 conference->no_video_avatar = switch_core_strdup(conference->pool, no_video_avatar);
3281 }
3282
3283
3284 conference->video_canvas_bgcolor = switch_core_strdup(conference->pool, video_canvas_bgcolor);
3285 conference->video_border_color = switch_core_strdup(conference->pool, video_border_color);
3286 conference->video_super_canvas_bgcolor = switch_core_strdup(conference->pool, video_super_canvas_bgcolor);
3287 conference->video_letterbox_bgcolor = switch_core_strdup(conference->pool, video_letterbox_bgcolor);
3288
3289 if (fps) {
3290 conference_video_set_fps(conference, fps);
3291 }
3292
3293 if (!conference->video_fps.ms) {
3294 conference_video_set_fps(conference, 30);
3295 }
3296
3297 if (video_codec_bandwidth) {
3298 if (!strcasecmp(video_codec_bandwidth, "auto")) {
3299 conference->video_codec_settings.video.bandwidth = switch_calc_bitrate(canvas_w, canvas_h, conference->video_quality, conference->video_fps.fps);
3300 } else {
3301 conference->video_codec_settings.video.bandwidth = switch_parse_bandwidth_string(video_codec_bandwidth);
3302 }
3303 }
3304
3305 conference->video_codec_settings.video.try_hardware_encoder = 1;
3306
3307 if (zstr(video_layout_name)) {
3308 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No video-layout-name specified, using " CONFERENCE_MUX_DEFAULT_LAYOUT "\n");
3309 video_layout_name = CONFERENCE_MUX_DEFAULT_LAYOUT;
3310 }
3311
3312 if (video_layout_name) {
3313 if (!strncasecmp(video_layout_name, "group:", 6)) {
3314 video_layout_group = video_layout_name + 6;
3315 }
3316 conference->video_layout_name = switch_core_strdup(conference->pool, video_layout_name);
3317 }
3318
3319 if (video_layout_group) {
3320 conference->video_layout_group = switch_core_strdup(conference->pool, video_layout_group);
3321 }
3322
3323 if (!conference_video_get_layout(conference, video_layout_name, video_layout_group)) {
3324 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid video-layout-name specified, using " CONFERENCE_MUX_DEFAULT_LAYOUT "\n");
3325 video_layout_name = CONFERENCE_MUX_DEFAULT_LAYOUT;
3326 video_layout_group = video_layout_name + 6;
3327 conference->video_layout_name = switch_core_strdup(conference->pool, video_layout_name);
3328 conference->video_layout_group = switch_core_strdup(conference->pool, video_layout_group);
3329 }
3330
3331 if (!conference_video_get_layout(conference, video_layout_name, video_layout_group)) {
3332 conference->video_layout_name = conference->video_layout_group = video_layout_group = video_layout_name = NULL;
3333 if (conference_video_get_layout(conference, conference->default_layout_name, NULL)) {
3334 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Defaulting to layout %s\n", conference->default_layout_name);
3335 video_layout_name = conference->video_layout_name = conference->default_layout_name;
3336 }
3337 }
3338
3339 if (!conference_video_get_layout(conference, video_layout_name, video_layout_group)) {
3340 conference->video_layout_name = conference->video_layout_group = video_layout_group = video_layout_name = NULL;
3341 conference->conference_video_mode = CONF_VIDEO_MODE_TRANSCODE;
3342 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid conference layout settings, falling back to transcode mode\n");
3343 } else {
3344 conference->canvas_width = canvas_w;
3345 conference->canvas_height = canvas_h;
3346 }
3347 }
3348
3349 if (conference->conference_video_mode == CONF_VIDEO_MODE_TRANSCODE || conference->conference_video_mode == CONF_VIDEO_MODE_MUX) {
3350 conference_utils_set_flag(conference, CFLAG_TRANSCODE_VIDEO);
3351 }
3352
3353 if (outcall_templ) {
3354 conference->outcall_templ = switch_core_strdup(conference->pool, outcall_templ);
3355 }
3356 conference->run_time = switch_epoch_time_now(NULL);
3357
3358 if (!zstr(conference_log_dir)) {
3359 char *path;
3360
3361 if (!strcmp(conference_log_dir, "auto")) {
3362 path = switch_core_sprintf(conference->pool, "%s%sconference_cdr", SWITCH_GLOBAL_dirs.log_dir, SWITCH_PATH_SEPARATOR);
3363 } else if (!switch_is_file_path(conference_log_dir)) {
3364 path = switch_core_sprintf(conference->pool, "%s%s%s", SWITCH_GLOBAL_dirs.log_dir, SWITCH_PATH_SEPARATOR, conference_log_dir);
3365 } else {
3366 path = switch_core_strdup(conference->pool, conference_log_dir);
3367 }
3368
3369 switch_dir_make_recursive(path, SWITCH_DEFAULT_DIR_PERMS, conference->pool);
3370 conference->log_dir = path;
3371
3372 }
3373
3374 if (!zstr(cdr_event_mode)) {
3375 if (!strcmp(cdr_event_mode, "content")) {
3376 conference->cdr_event_mode = CDRE_AS_CONTENT;
3377 } else if (!strcmp(cdr_event_mode, "file")) {
3378 if (!zstr(conference->log_dir)) {
3379 conference->cdr_event_mode = CDRE_AS_FILE;
3380 } else {
3381 conference->cdr_event_mode = CDRE_NONE;
3382 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "'cdr-log-dir' parameter not set; CDR event mode 'file' ignored");
3383 }
3384 } else {
3385 conference->cdr_event_mode = CDRE_NONE;
3386 }
3387 }
3388
3389 if (!zstr(perpetual_sound)) {
3390 conference->perpetual_sound = switch_core_strdup(conference->pool, perpetual_sound);
3391 }
3392
3393 conference->mflags[MFLAG_CAN_SPEAK] = conference->mflags[MFLAG_CAN_HEAR] = conference->mflags[MFLAG_CAN_BE_SEEN] = conference->mflags[MFLAG_CAN_SEE] = 1;
3394
3395 if (!zstr(moh_sound) && switch_is_moh(moh_sound)) {
3396 conference->moh_sound = switch_core_strdup(conference->pool, moh_sound);
3397 }
3398
3399 if (member_flags) {
3400 conference_utils_set_mflags(member_flags, conference->mflags);
3401 }
3402
3403 if (conference_flags) {
3404 conference_utils_set_cflags(conference_flags, conference->flags);
3405 }
3406
3407 if (!zstr(sound_prefix)) {
3408 conference->sound_prefix = switch_core_strdup(conference->pool, sound_prefix);
3409 } else {
3410 const char *val;
3411 if ((val = switch_channel_get_variable(channel, "sound_prefix")) && !zstr(val)) {
3412 /* if no sound_prefix was set, use the channel sound_prefix */
3413 conference->sound_prefix = switch_core_strdup(conference->pool, val);
3414 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "using channel sound prefix: %s\n", conference->sound_prefix);
3415 }
3416 }
3417
3418 if (!zstr(enter_sound)) {
3419 conference->enter_sound = switch_core_strdup(conference->pool, enter_sound);
3420 }
3421
3422 if (!zstr(member_enter_sound)) {
3423 conference->member_enter_sound = switch_core_strdup(conference->pool, member_enter_sound);
3424 }
3425
3426 if (!zstr(exit_sound)) {
3427 conference->exit_sound = switch_core_strdup(conference->pool, exit_sound);
3428 }
3429
3430 if (!zstr(muted_sound)) {
3431 conference->muted_sound = switch_core_strdup(conference->pool, muted_sound);
3432 }
3433
3434 if (zstr(mute_detect_sound)) {
3435 if (!zstr(muted_sound)) {
3436 conference->mute_detect_sound = switch_core_strdup(conference->pool, muted_sound);
3437 }
3438 } else {
3439 conference->mute_detect_sound = switch_core_strdup(conference->pool, mute_detect_sound);
3440 }
3441
3442 if (!zstr(unmuted_sound)) {
3443 conference->unmuted_sound = switch_core_strdup(conference->pool, unmuted_sound);
3444 }
3445
3446 if (!zstr(kicked_sound)) {
3447 conference->kicked_sound = switch_core_strdup(conference->pool, kicked_sound);
3448 }
3449
3450 if (!zstr(pin_sound)) {
3451 conference->pin_sound = switch_core_strdup(conference->pool, pin_sound);
3452 }
3453
3454 if (!zstr(bad_pin_sound)) {
3455 conference->bad_pin_sound = switch_core_strdup(conference->pool, bad_pin_sound);
3456 }
3457
3458 if (!zstr(deaf_sound)) {
3459 conference->deaf_sound = switch_core_strdup(conference->pool, deaf_sound);
3460 }
3461
3462 if (!zstr(undeaf_sound)) {
3463 conference->undeaf_sound = switch_core_strdup(conference->pool, undeaf_sound);
3464 }
3465
3466 if (!zstr(blind_sound)) {
3467 conference->blind_sound = switch_core_strdup(conference->pool, blind_sound);
3468 }
3469
3470 if (!zstr(unblind_sound)) {
3471 conference->unblind_sound = switch_core_strdup(conference->pool, unblind_sound);
3472 }
3473
3474 if (!zstr(pin)) {
3475 conference->pin = switch_core_strdup(conference->pool, pin);
3476 }
3477
3478 if (!zstr(mpin)) {
3479 conference->mpin = switch_core_strdup(conference->pool, mpin);
3480 }
3481
3482 if (!zstr(alone_sound)) {
3483 conference->alone_sound = switch_core_strdup(conference->pool, alone_sound);
3484 }
3485
3486 if (!zstr(locked_sound)) {
3487 conference->locked_sound = switch_core_strdup(conference->pool, locked_sound);
3488 }
3489
3490 if (!zstr(is_locked_sound)) {
3491 conference->is_locked_sound = switch_core_strdup(conference->pool, is_locked_sound);
3492 }
3493
3494 if (!zstr(is_unlocked_sound)) {
3495 conference->is_unlocked_sound = switch_core_strdup(conference->pool, is_unlocked_sound);
3496 }
3497
3498 if (!zstr(energy_level)) {
3499 conference->energy_level = atoi(energy_level);
3500 if (conference->energy_level < 0) {
3501 conference->energy_level = 0;
3502 }
3503 }
3504
3505 if (!zstr(max_energy_level_mute_ms)) {
3506 int mute_ms = atoi(max_energy_level_mute_ms);
3507
3508 if (mute_ms > 0) {
3509 conference->burst_mute_count = mute_ms / interval;
3510 }
3511 }
3512
3513 conference->max_energy_hit_trigger = 5;
3514
3515 if (!zstr(max_energy_level)) {
3516 conference->max_energy_hit_trigger = atoi(max_energy_hit_trigger);
3517 if (conference->max_energy_hit_trigger < 0) {
3518 conference->max_energy_hit_trigger = 0;
3519 }
3520
3521 conference->max_energy_level = atoi(max_energy_level);
3522 if (conference->max_energy_level < 0) {
3523 conference->max_energy_level = 0;
3524 }
3525
3526
3527 if (conference->energy_level && conference->max_energy_level < conference->energy_level) {
3528 conference->max_energy_level = 0;
3529 }
3530 }
3531
3532 if (!zstr(auto_energy_sec)) {
3533 conference->auto_energy_sec = atoi(auto_energy_sec);
3534 if (conference->auto_energy_sec < 0) {
3535 conference->auto_energy_sec = 0;
3536 }
3537 }
3538
3539 if (!conference->auto_energy_sec) {
3540 conference->auto_energy_sec = 5;
3541 }
3542
3543 if (!zstr(auto_energy_level)) {
3544 conference->auto_energy_level = atoi(auto_energy_level);
3545 if (conference->auto_energy_level < 0) {
3546 conference->auto_energy_level = 0;
3547 }
3548
3549 if (!conference->energy_level) {
3550 conference->energy_level = conference->auto_energy_level / 2;
3551 }
3552 }
3553
3554 if (!zstr(maxmember_sound)) {
3555 conference->maxmember_sound = switch_core_strdup(conference->pool, maxmember_sound);
3556 }
3557 /* its going to be 0 by default, set to a value otherwise so this should be safe */
3558 conference->max_members = max_members;
3559 conference->announce_count = announce_count;
3560
3561 conference->name = switch_core_strdup(conference->pool, name);
3562
3563 if ((name_domain = strchr(conference->name, '@'))) {
3564 name_domain++;
3565 conference->domain = switch_core_strdup(conference->pool, name_domain);
3566 } else if (domain) {
3567 conference->domain = switch_core_strdup(conference->pool, domain);
3568 } else if (presence_id && (name_domain = strchr(presence_id, '@'))) {
3569 name_domain++;
3570 conference->domain = switch_core_strdup(conference->pool, name_domain);
3571 } else {
3572 conference->domain = "cluecon.com";
3573 }
3574
3575 conference->chat_id = switch_core_sprintf(conference->pool, "conf+%s@%s", conference->name, conference->domain);
3576
3577 conference->channels = channels;
3578 conference->rate = rate;
3579 conference->interval = interval;
3580 conference->ivr_dtmf_timeout = ivr_dtmf_timeout;
3581 conference->ivr_input_timeout = ivr_input_timeout;
3582
3583
3584 conference->agc_level = 0;
3585 conference->agc_low_energy_level = 0;
3586 conference->agc_margin = 20;
3587 conference->agc_change_factor = 3;
3588 conference->agc_period_len = (1000 / conference->interval) * 2;
3589
3590
3591 if (agc_level) {
3592 tmp = atoi(agc_level);
3593 if (tmp > 0) {
3594 conference->agc_level = tmp;
3595 }
3596 }
3597
3598 if (agc_low_energy_level) {
3599 tmp = atoi(agc_low_energy_level);
3600 if (tmp > 0) {
3601 conference->agc_low_energy_level = tmp;
3602 }
3603 }
3604
3605 if (agc_margin) {
3606 tmp = atoi(agc_margin);
3607 if (tmp > 0) {
3608 conference->agc_margin = tmp;
3609 }
3610 }
3611
3612 if (agc_change_factor) {
3613 tmp = atoi(agc_change_factor);
3614 if (tmp > 0) {
3615 conference->agc_change_factor = tmp;
3616 }
3617 }
3618
3619 if (agc_period_len) {
3620 tmp = atoi(agc_period_len);
3621 if (tmp > 0) {
3622 conference->agc_period_len = (1000 / conference->interval) * tmp;
3623 }
3624 }
3625
3626 if (video_auto_floor_msec) {
3627 conference->video_floor_packets = video_auto_floor_msec / conference->interval;
3628 }
3629
3630 conference->eflags = 0xFFFFFFFF;
3631
3632 if (!zstr(suppress_events)) {
3633 conference_utils_clear_eflags(suppress_events, &conference->eflags);
3634 }
3635
3636 if (!zstr(auto_record)) {
3637 conference->auto_record = switch_core_strdup(conference->pool, auto_record);
3638 }
3639
3640 if (!zstr(recording_metadata)) {
3641 conference->recording_metadata = switch_core_strdup(conference->pool, recording_metadata);
3642 }
3643
3644 conference->min_recording_participants = min_recording_participants;
3645
3646 if (!zstr(desc)) {
3647 conference->desc = switch_core_strdup(conference->pool, desc);
3648 }
3649
3650 if (!zstr(terminate_on_silence)) {
3651 conference->terminate_on_silence = atoi(terminate_on_silence);
3652 }
3653 if (!zstr(endconference_grace_time)) {
3654 conference->endconference_grace_time = atoi(endconference_grace_time);
3655 }
3656
3657 if (!zstr(verbose_events) && switch_true(verbose_events)) {
3658 conference->verbose_events = 1;
3659 }
3660
3661 /* Create the conference unique identifier */
3662 switch_uuid_get(&uuid);
3663 switch_uuid_format(uuid_str, &uuid);
3664 conference->uuid_str = switch_core_strdup(conference->pool, uuid_str);
3665
3666 /* Set enter sound and exit sound flags so that default is on */
3667 conference_utils_set_flag(conference, CFLAG_ENTER_SOUND);
3668 conference_utils_set_flag(conference, CFLAG_EXIT_SOUND);
3669
3670 /* Activate the conference mutex for exclusivity */
3671 switch_mutex_init(&conference->mutex, SWITCH_MUTEX_NESTED, conference->pool);
3672 switch_mutex_init(&conference->flag_mutex, SWITCH_MUTEX_NESTED, conference->pool);
3673 switch_mutex_init(&conference->file_mutex, SWITCH_MUTEX_NESTED, conference->pool);
3674 switch_thread_rwlock_create(&conference->rwlock, conference->pool);
3675 switch_mutex_init(&conference->member_mutex, SWITCH_MUTEX_NESTED, conference->pool);
3676 switch_mutex_init(&conference->canvas_mutex, SWITCH_MUTEX_NESTED, conference->pool);
3677
3678 switch_mutex_lock(conference_globals.hash_mutex);
3679 conference_utils_set_flag(conference, CFLAG_INHASH);
3680 switch_core_hash_insert(conference_globals.conference_hash, conference->name, conference);
3681 switch_mutex_unlock(conference_globals.hash_mutex);
3682
3683 conference->super_canvas_label_layers = video_super_canvas_label_layers;
3684 conference->super_canvas_show_all_layers = video_super_canvas_show_all_layers;
3685
3686
3687 if (conference_utils_test_flag(conference, CFLAG_LIVEARRAY_SYNC)) {
3688 char *p;
3689
3690 if (strchr(conference->name, '@')) {
3691 conference->la_event_channel = switch_core_sprintf(conference->pool, "conference-liveArray.%s", conference->name);
3692 conference->chat_event_channel = switch_core_sprintf(conference->pool, "conference-chat.%s", conference->name);
3693 conference->info_event_channel = switch_core_sprintf(conference->pool, "conference-info.%s", conference->name);
3694 conference->mod_event_channel = switch_core_sprintf(conference->pool, "conference-mod.%s", conference->name);
3695 } else {
3696 conference->la_event_channel = switch_core_sprintf(conference->pool, "conference-liveArray.%s@%s", conference->name, conference->domain);
3697 conference->chat_event_channel = switch_core_sprintf(conference->pool, "conference-chat.%s@%s", conference->name, conference->domain);
3698 conference->info_event_channel = switch_core_sprintf(conference->pool, "conference-info.%s@%s", conference->name, conference->domain);
3699 conference->mod_event_channel = switch_core_sprintf(conference->pool, "conference-mod.%s@%s", conference->name, conference->domain);
3700 }
3701
3702 conference->la_name = switch_core_strdup(conference->pool, conference->name);
3703 if ((p = strchr(conference->la_name, '@'))) {
3704 *p = '\0';
3705 }
3706
3707 switch_live_array_create(conference->la_event_channel, conference->la_name, conference_globals.event_channel_id, &conference->la);
3708 switch_live_array_set_user_data(conference->la, conference);
3709 switch_live_array_set_command_handler(conference->la, conference_event_la_command_handler);
3710 }
3711
3712
3713
3714 if (video_canvas_count < 1) video_canvas_count = 1;
3715
3716 if (conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS) && video_canvas_count > 1) {
3717 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Personal Canvas and Multi-Canvas modes are not compatable. 1 canvas will be used.\n");
3718 video_canvas_count = 1;
3719 }
3720
3721 if (conference->conference_video_mode == CONF_VIDEO_MODE_MUX) {
3722 video_layout_t *vlayout = conference_video_get_layout(conference, conference->video_layout_name, conference->video_layout_group);
3723
3724 if (!vlayout) {
3725 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot find layout\n");
3726 conference->video_layout_name = conference->video_layout_group = NULL;
3727 conference_utils_clear_flag(conference, CFLAG_VIDEO_MUXING);
3728 } else {
3729 int j;
3730
3731 for (j = 0; j < video_canvas_count; j++) {
3732 mcu_canvas_t *canvas = NULL;
3733
3734 switch_mutex_lock(conference->canvas_mutex);
3735 conference_video_init_canvas(conference, vlayout, &canvas);
3736 conference_video_attach_canvas(conference, canvas, 0);
3737 conference_video_launch_muxing_thread(conference, canvas, 0);
3738 switch_mutex_unlock(conference->canvas_mutex);
3739 }
3740
3741 if (conference->canvas_count > 1) {
3742 video_layout_t *svlayout = conference_video_get_layout(conference, NULL, CONFERENCE_MUX_DEFAULT_SUPER_LAYOUT);
3743 mcu_canvas_t *canvas = NULL;
3744
3745 if (svlayout) {
3746 switch_mutex_lock(conference->canvas_mutex);
3747 conference_video_init_canvas(conference, svlayout, &canvas);
3748 conference_video_set_canvas_bgcolor(canvas, conference->video_super_canvas_bgcolor);
3749 conference_video_attach_canvas(conference, canvas, 1);
3750 conference_video_launch_muxing_thread(conference, canvas, 1);
3751 switch_mutex_unlock(conference->canvas_mutex);
3752 }
3753 }
3754
3755 if (cfg.profile) {
3756 for (xml_kvp = switch_xml_child(cfg.profile, "video-codec-group"); xml_kvp; xml_kvp = xml_kvp->next) {
3757 char *name = (char *) switch_xml_attr_soft(xml_kvp, "name");
3758 char *bw = (char *) switch_xml_attr_soft(xml_kvp, "bandwidth");
3759 char *fps = (char *) switch_xml_attr_soft(xml_kvp, "fps-divisor");
3760 char *res = (char *) switch_xml_attr_soft(xml_kvp, "res-divisor");
3761
3762 if (name && bw) {
3763 const char *str = switch_core_sprintf(conference->pool, "%s%s%s%s%s", bw,
3764 !zstr(res) ? ":" : "", res, !zstr(fps) ? ":" : "", fps);
3765
3766 const char *gname = switch_core_sprintf(conference->pool, "group-%s", name);
3767
3768 conference_set_variable(conference, gname, str);
3769 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Video codec group preset %s set to [%s]\n", gname, str);
3770 }
3771
3772 }
3773
3774 }
3775
3776 }
3777 }
3778
3779 if (cfg.profile) {
3780 switch_xml_t xml_profile_variables;
3781 if ((xml_profile_variables = switch_xml_child(cfg.profile, "variables")) != NULL) {
3782 for (xml_kvp = switch_xml_child(xml_profile_variables, "variable"); xml_kvp; xml_kvp = xml_kvp->next) {
3783 char *var = (char *) switch_xml_attr_soft(xml_kvp, "name");
3784 char *val = (char *) switch_xml_attr_soft(xml_kvp, "value");
3785 if (var && val) {
3786 conference_set_variable(conference, var, val);
3787 }
3788 }
3789 }
3790 }
3791
3792 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT);
3793 conference_event_add_data(conference, event);
3794 if (conference->verbose_events && channel) {
3795 switch_channel_event_set_data(channel, event);
3796 }
3797 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "conference-create");
3798 switch_event_fire(&event);
3799
3800 end:
3801
3802 switch_mutex_unlock(conference_globals.hash_mutex);
3803
3804 return conference;
3805 }
3806
conference_send_presence(conference_obj_t * conference)3807 void conference_send_presence(conference_obj_t *conference)
3808 {
3809 switch_event_t *event;
3810
3811 if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) {
3812 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", CONF_CHAT_PROTO);
3813 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", conference->name);
3814 if (strchr(conference->name, '@')) {
3815 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", conference->name);
3816 } else {
3817 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", conference->name, conference->domain);
3818 }
3819
3820 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
3821 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alt_event_type", "dialog");
3822 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_count", "%d", EC++);
3823 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "unique-id", conference->name);
3824
3825 if (conference->count) {
3826 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "force-status", "Active (%d caller%s)", conference->count, conference->count == 1 ? "" : "s");
3827 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_ROUTING");
3828 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", conference->count == 1 ? "early" : "confirmed");
3829 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "presence-call-direction", conference->count == 1 ? "outbound" : "inbound");
3830 } else {
3831 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", "Inactive");
3832 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "channel-state", "CS_HANGUP");
3833 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "answer-state", "terminated");
3834 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-direction", "inbound");
3835 }
3836
3837
3838
3839 switch_event_fire(&event);
3840 }
3841
3842 }
3843 #if 0
3844 uint32_t conference_kickall_matching_var(conference_obj_t *conference, const char *var, const char *val)
3845 {
3846 conference_member_t *member = NULL;
3847 const char *vval = NULL;
3848 uint32_t r = 0;
3849
3850 switch_mutex_lock(conference->mutex);
3851 switch_mutex_lock(conference->member_mutex);
3852
3853 for (member = conference->members; member; member = member->next) {
3854 switch_channel_t *channel = NULL;
3855
3856 if (conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
3857 continue;
3858 }
3859
3860 channel = switch_core_session_get_channel(member->session);
3861 vval = switch_channel_get_variable(channel, var);
3862
3863 if (vval && !strcmp(vval, val)) {
3864 conference_utils_member_set_flag_locked(member, MFLAG_KICKED);
3865 conference_utils_member_clear_flag_locked(member, MFLAG_RUNNING);
3866 switch_core_session_kill_channel(member->session, SWITCH_SIG_BREAK);
3867 r++;
3868 }
3869
3870 }
3871
3872 switch_mutex_unlock(conference->member_mutex);
3873 switch_mutex_unlock(conference->mutex);
3874
3875 return r;
3876 }
3877 #endif
3878
send_presence(switch_event_types_t id)3879 void send_presence(switch_event_types_t id)
3880 {
3881 switch_xml_t cxml, cfg, advertise, room;
3882 switch_event_t *params = NULL;
3883
3884 switch_event_create(¶ms, SWITCH_EVENT_COMMAND);
3885 switch_assert(params);
3886 switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "presence", "true");
3887
3888
3889 /* Open the config from the xml registry */
3890 if (!(cxml = switch_xml_open_cfg(mod_conference_cf_name, &cfg, params))) {
3891 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", mod_conference_cf_name);
3892 goto done;
3893 }
3894
3895 if ((advertise = switch_xml_child(cfg, "advertise"))) {
3896 for (room = switch_xml_child(advertise, "room"); room; room = room->next) {
3897 char *name = (char *) switch_xml_attr_soft(room, "name");
3898 char *status = (char *) switch_xml_attr_soft(room, "status");
3899 switch_event_t *event;
3900
3901 if (name && switch_event_create(&event, id) == SWITCH_STATUS_SUCCESS) {
3902 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", CONF_CHAT_PROTO);
3903 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", name);
3904 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", name);
3905 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "force-status", status ? status : "Available");
3906 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "rpid", "unknown");
3907 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "event_type", "presence");
3908 switch_event_fire(&event);
3909 }
3910 }
3911 }
3912
3913 done:
3914 switch_event_destroy(¶ms);
3915
3916 /* Release the config registry handle */
3917 if (cxml) {
3918 switch_xml_free(cxml);
3919 cxml = NULL;
3920 }
3921 }
3922
3923 /* Called by FreeSWITCH when the module loads */
SWITCH_MODULE_LOAD_FUNCTION(mod_conference_load)3924 SWITCH_MODULE_LOAD_FUNCTION(mod_conference_load)
3925 {
3926 char *p = NULL;
3927 switch_chat_interface_t *chat_interface;
3928 switch_api_interface_t *api_interface;
3929 switch_application_interface_t *app_interface;
3930 switch_status_t status = SWITCH_STATUS_SUCCESS;
3931
3932 memset(&conference_globals, 0, sizeof(conference_globals));
3933
3934 /* Connect my internal structure to the blank pointer passed to me */
3935 *module_interface = switch_loadable_module_create_module_interface(pool, modname);
3936
3937 switch_console_add_complete_func("::conference::conference_list_conferences", conference_list_conferences);
3938
3939
3940 switch_event_channel_bind("conference", conference_event_channel_handler, &conference_globals.event_channel_id, NULL);
3941 switch_event_channel_bind("conference-liveArray", conference_event_la_channel_handler, &conference_globals.event_channel_id, NULL);
3942 switch_event_channel_bind("conference-mod", conference_event_mod_channel_handler, &conference_globals.event_channel_id, NULL);
3943 switch_event_channel_bind("conference-chat", conference_event_chat_channel_handler, &conference_globals.event_channel_id, NULL);
3944
3945 if ( conference_api_sub_syntax(&api_syntax) != SWITCH_STATUS_SUCCESS) {
3946 return SWITCH_STATUS_TERM;
3947 }
3948
3949 /* create/register custom event message type */
3950 if (switch_event_reserve_subclass(CONF_EVENT_MAINT) != SWITCH_STATUS_SUCCESS) {
3951 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", CONF_EVENT_MAINT);
3952 return SWITCH_STATUS_TERM;
3953 }
3954
3955 /* Setup the pool */
3956 conference_globals.conference_pool = pool;
3957
3958 /* Setup a hash to store conferences by name */
3959 switch_core_hash_init(&conference_globals.conference_hash);
3960 switch_mutex_init(&conference_globals.conference_mutex, SWITCH_MUTEX_NESTED, conference_globals.conference_pool);
3961 switch_mutex_init(&conference_globals.id_mutex, SWITCH_MUTEX_NESTED, conference_globals.conference_pool);
3962 switch_mutex_init(&conference_globals.hash_mutex, SWITCH_MUTEX_NESTED, conference_globals.conference_pool);
3963 switch_mutex_init(&conference_globals.setup_mutex, SWITCH_MUTEX_NESTED, conference_globals.conference_pool);
3964
3965 /* Subscribe to presence request events */
3966 if (switch_event_bind(modname, SWITCH_EVENT_PRESENCE_PROBE, SWITCH_EVENT_SUBCLASS_ANY, conference_event_pres_handler, NULL) != SWITCH_STATUS_SUCCESS) {
3967 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't subscribe to presence request events!\n");
3968 }
3969
3970 if (switch_event_bind(modname, SWITCH_EVENT_CONFERENCE_DATA_QUERY, SWITCH_EVENT_SUBCLASS_ANY, conference_data_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
3971 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't subscribe to conference data query events!\n");
3972 }
3973
3974 if (switch_event_bind(modname, SWITCH_EVENT_CALL_SETUP_REQ, SWITCH_EVENT_SUBCLASS_ANY, conference_event_call_setup_handler, NULL) != SWITCH_STATUS_SUCCESS) {
3975 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't subscribe to conference data query events!\n");
3976 }
3977
3978 SWITCH_ADD_API(api_interface, "conference", "Conference module commands", conference_api_main, p);
3979 SWITCH_ADD_APP(app_interface, mod_conference_app_name, mod_conference_app_name, NULL, conference_function, NULL, SAF_SUPPORT_TEXT_ONLY);
3980 SWITCH_ADD_APP(app_interface, "conference_set_auto_outcall", "conference_set_auto_outcall", NULL, conference_auto_function, NULL, SAF_NONE);
3981 SWITCH_ADD_CHAT(chat_interface, CONF_CHAT_PROTO, chat_send);
3982
3983 send_presence(SWITCH_EVENT_PRESENCE_IN);
3984
3985 conference_globals.running = 1;
3986 /* indicate that the module should continue to be loaded */
3987 return status;
3988 }
3989
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_conference_shutdown)3990 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_conference_shutdown)
3991 {
3992 if (conference_globals.running) {
3993
3994 /* signal all threads to shutdown */
3995 conference_globals.running = 0;
3996
3997 switch_event_channel_unbind(NULL, conference_event_channel_handler, NULL);
3998 switch_event_channel_unbind(NULL, conference_event_la_channel_handler, NULL);
3999 switch_event_channel_unbind(NULL, conference_event_mod_channel_handler, NULL);
4000 switch_event_channel_unbind(NULL, conference_event_chat_channel_handler, NULL);
4001
4002 switch_console_del_complete_func("::conference::conference_list_conferences");
4003
4004 /* wait for all threads */
4005 while (conference_globals.threads) {
4006 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Waiting for %d threads\n", conference_globals.threads);
4007 switch_yield(100000);
4008 }
4009
4010 switch_event_unbind_callback(conference_event_pres_handler);
4011 switch_event_unbind_callback(conference_data_event_handler);
4012 switch_event_unbind_callback(conference_event_call_setup_handler);
4013 switch_event_free_subclass(CONF_EVENT_MAINT);
4014
4015 /* free api interface help ".syntax" field string */
4016 switch_safe_free(api_syntax);
4017 }
4018 switch_core_hash_destroy(&conference_globals.conference_hash);
4019
4020 return SWITCH_STATUS_SUCCESS;
4021 }
4022
4023 /* For Emacs:
4024 * Local Variables:
4025 * mode:c
4026 * indent-tabs-mode:t
4027 * tab-width:4
4028 * c-basic-offset:4
4029 * End:
4030 * For VIM:
4031 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
4032 */
4033