2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2007-2008, Digium, Inc.
6 * Joshua Colp <jcolp@digium.com>
7 * David Vossel <dvossel@digium.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
22 * \brief Conference Bridge application
24 * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
25 * \author\verbatim David Vossel <dvossel@digium.com> \endverbatim
27 * This is a conference bridge application utilizing the bridging core.
28 * \ingroup applications
32 <support_level>core</support_level>
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
45 #include "asterisk/cli.h"
46 #include "asterisk/file.h"
47 #include "asterisk/channel.h"
48 #include "asterisk/pbx.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/module.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/bridging.h"
53 #include "asterisk/musiconhold.h"
54 #include "asterisk/say.h"
55 #include "asterisk/audiohook.h"
56 #include "asterisk/astobj2.h"
57 #include "confbridge/include/confbridge.h"
58 #include "asterisk/paths.h"
59 #include "asterisk/manager.h"
60 #include "asterisk/test.h"
63 <application name="ConfBridge" language="en_US">
65 Conference bridge application.
68 <parameter name="confno">
69 <para>The conference number</para>
71 <parameter name="bridge_profile">
72 <para>The bridge profile name from confbridge.conf. When left blank, a dynamically built bridge profile created by the CONFBRIDGE dialplan function is searched for on the channel and used. If no dynamic profile is present, the 'default_bridge' profile found in confbridge.conf is used. </para>
73 <para>It is important to note that while user profiles may be unique for each participant, mixing bridge profiles on a single conference is _NOT_ recommended and will produce undefined results.</para>
75 <parameter name="user_profile">
76 <para>The user profile name from confbridge.conf. When left blank, a dynamically built user profile created by the CONFBRIDGE dialplan function is searched for on the channel and used. If no dynamic profile is present, the 'default_user' profile found in confbridge.conf is used.</para>
78 <parameter name="menu">
79 <para>The name of the DTMF menu in confbridge.conf to be applied to this channel. No menu is applied by default if this option is left blank.</para>
83 <para>Enters the user into a specified conference bridge. The user can exit the conference by hangup or DTMF menu option.</para>
86 <ref type="application">ConfBridge</ref>
87 <ref type="function">CONFBRIDGE</ref>
88 <ref type="function">CONFBRIDGE_INFO</ref>
91 <function name="CONFBRIDGE" language="en_US">
93 Set a custom dynamic bridge and user profile on a channel for the ConfBridge application using the same options defined in confbridge.conf.
96 <parameter name="type" required="true">
97 <para>Type refers to which type of profile the option belongs too. Type can be <literal>bridge</literal> or <literal>user</literal>.</para>
99 <parameter name="option" required="true">
100 <para>Option refers to <filename>confbridge.conf</filename> option that is being set dynamically on this channel.</para>
104 <para>---- Example 1 ----</para>
105 <para>In this example the custom set user profile on this channel will automatically be used by the ConfBridge app.</para>
106 <para>exten => 1,1,Answer() </para>
107 <para>exten => 1,n,Set(CONFBRIDGE(user,announce_join_leave)=yes)</para>
108 <para>exten => 1,n,Set(CONFBRIDGE(user,startmuted)=yes)</para>
109 <para>exten => 1,n,ConfBridge(1) </para>
110 <para>---- Example 2 ----</para>
111 <para>This example shows how to use a predefined user or bridge profile in confbridge.conf as a template for a dynamic profile. Here we make a admin/marked user out of the default_user profile that is already defined in confbridge.conf.</para>
112 <para>exten => 1,1,Answer() </para>
113 <para>exten => 1,n,Set(CONFBRIDGE(user,template)=default_user)</para>
114 <para>exten => 1,n,Set(CONFBRIDGE(user,admin)=yes)</para>
115 <para>exten => 1,n,Set(CONFBRIDGE(user,marked)=yes)</para>
116 <para>exten => 1,n,ConfBridge(1)</para>
119 <function name="CONFBRIDGE_INFO" language="en_US">
121 Get information about a ConfBridge conference.
124 <parameter name="type" required="true">
125 <para>Type can be <literal>parties</literal>, <literal>admins</literal>, <literal>marked</literal>, or <literal>locked</literal>.</para>
127 <parameter name="conf" required="true">
128 <para>Conf refers to the name of the conference being referenced.</para>
132 <para>This function returns a non-negative integer for valid conference identifiers (0 or 1 for <literal>locked</literal>) and "" for invalid conference identifiers.</para>
135 <manager name="ConfbridgeList" language="en_US">
137 List participants in a conference.
140 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
141 <parameter name="Conference" required="false">
142 <para>Conference number.</para>
146 <para>Lists all users in a particular ConfBridge conference.
147 ConfbridgeList will follow as separate events, followed by a final event called
148 ConfbridgeListComplete.</para>
151 <manager name="ConfbridgeListRooms" language="en_US">
153 List active conferences.
156 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
159 <para>Lists data about all active conferences.
160 ConfbridgeListRooms will follow as separate events, followed by a final event called
161 ConfbridgeListRoomsComplete.</para>
164 <manager name="ConfbridgeMute" language="en_US">
166 Mute a Confbridge user.
169 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
170 <parameter name="Conference" required="true" />
171 <parameter name="Channel" required="true" />
176 <manager name="ConfbridgeUnmute" language="en_US">
178 Unmute a Confbridge user.
181 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
182 <parameter name="Conference" required="true" />
183 <parameter name="Channel" required="true" />
188 <manager name="ConfbridgeKick" language="en_US">
190 Kick a Confbridge user.
193 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
194 <parameter name="Conference" required="true" />
195 <parameter name="Channel" required="true" />
200 <manager name="ConfbridgeLock" language="en_US">
202 Lock a Confbridge conference.
205 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
206 <parameter name="Conference" required="true" />
211 <manager name="ConfbridgeUnlock" language="en_US">
213 Unlock a Confbridge conference.
216 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
217 <parameter name="Conference" required="true" />
222 <manager name="ConfbridgeStartRecord" language="en_US">
224 Start recording a Confbridge conference.
227 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
228 <parameter name="Conference" required="true" />
229 <parameter name="RecordFile" required="false" />
232 <para>Start recording a conference. If recording is already present an error will be returned. If RecordFile is not provided, the default record file specified in the conference's bridge profile will be used, if that is not present either a file will automatically be generated in the monitor directory.</para>
235 <manager name="ConfbridgeStopRecord" language="en_US">
237 Stop recording a Confbridge conference.
240 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
241 <parameter name="Conference" required="true" />
246 <manager name="ConfbridgeSetSingleVideoSrc" language="en_US">
248 Set a conference user as the single video source distributed to all other participants.
251 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
252 <parameter name="Conference" required="true" />
253 <parameter name="Channel" required="true" />
262 * \par Playing back a file to a channel in a conference
263 * You might notice in this application that while playing a sound file
264 * to a channel the actual conference bridge lock is not held. This is done so
265 * that other channels are not blocked from interacting with the conference bridge.
266 * Unfortunately because of this it is possible for things to change after the sound file
267 * is done being played. Data must therefore be checked after reacquiring the conference
268 * bridge lock if it is important.
271 static const char app[] = "ConfBridge";
273 /* Number of buckets our conference bridges container can have */
274 #define CONFERENCE_BRIDGE_BUCKETS 53
276 /*! \brief Container to hold all conference bridges in progress */
277 static struct ao2_container *conference_bridges;
279 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename);
280 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number);
281 static int execute_menu_entry(struct conference_bridge *conference_bridge,
282 struct conference_bridge_user *conference_bridge_user,
283 struct ast_bridge_channel *bridge_channel,
284 struct conf_menu_entry *menu_entry,
285 struct conf_menu *menu);
287 /*! \brief Hashing function used for conference bridges container */
288 static int conference_bridge_hash_cb(const void *obj, const int flags)
290 const struct conference_bridge *conference_bridge = obj;
291 return ast_str_case_hash(conference_bridge->name);
294 /*! \brief Comparison function used for conference bridges container */
295 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
297 const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
298 return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
301 const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
304 case CONF_SOUND_HAS_JOINED:
305 return S_OR(custom_sounds->hasjoin, "conf-hasjoin");
306 case CONF_SOUND_HAS_LEFT:
307 return S_OR(custom_sounds->hasleft, "conf-hasleft");
308 case CONF_SOUND_KICKED:
309 return S_OR(custom_sounds->kicked, "conf-kicked");
310 case CONF_SOUND_MUTED:
311 return S_OR(custom_sounds->muted, "conf-muted");
312 case CONF_SOUND_UNMUTED:
313 return S_OR(custom_sounds->unmuted, "conf-unmuted");
314 case CONF_SOUND_ONLY_ONE:
315 return S_OR(custom_sounds->onlyone, "conf-onlyone");
316 case CONF_SOUND_THERE_ARE:
317 return S_OR(custom_sounds->thereare, "conf-thereare");
318 case CONF_SOUND_OTHER_IN_PARTY:
319 return S_OR(custom_sounds->otherinparty, "conf-otherinparty");
320 case CONF_SOUND_PLACE_IN_CONF:
321 return S_OR(custom_sounds->placeintoconf, "conf-placeintoconf");
322 case CONF_SOUND_WAIT_FOR_LEADER:
323 return S_OR(custom_sounds->waitforleader, "conf-waitforleader");
324 case CONF_SOUND_LEADER_HAS_LEFT:
325 return S_OR(custom_sounds->leaderhasleft, "conf-leaderhasleft");
326 case CONF_SOUND_GET_PIN:
327 return S_OR(custom_sounds->getpin, "conf-getpin");
328 case CONF_SOUND_INVALID_PIN:
329 return S_OR(custom_sounds->invalidpin, "conf-invalidpin");
330 case CONF_SOUND_ONLY_PERSON:
331 return S_OR(custom_sounds->onlyperson, "conf-onlyperson");
332 case CONF_SOUND_LOCKED:
333 return S_OR(custom_sounds->locked, "conf-locked");
334 case CONF_SOUND_LOCKED_NOW:
335 return S_OR(custom_sounds->lockednow, "conf-lockednow");
336 case CONF_SOUND_UNLOCKED_NOW:
337 return S_OR(custom_sounds->unlockednow, "conf-unlockednow");
338 case CONF_SOUND_ERROR_MENU:
339 return S_OR(custom_sounds->errormenu, "conf-errormenu");
340 case CONF_SOUND_JOIN:
341 return S_OR(custom_sounds->join, "confbridge-join");
342 case CONF_SOUND_LEAVE:
343 return S_OR(custom_sounds->leave, "confbridge-leave");
344 case CONF_SOUND_PARTICIPANTS_MUTED:
345 return S_OR(custom_sounds->participantsmuted, "conf-now-muted");
346 case CONF_SOUND_PARTICIPANTS_UNMUTED:
347 return S_OR(custom_sounds->participantsunmuted, "conf-now-unmuted");
353 static struct ast_frame *rec_read(struct ast_channel *ast)
355 return &ast_null_frame;
357 static int rec_write(struct ast_channel *ast, struct ast_frame *f)
361 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
362 static struct ast_channel_tech record_tech = {
363 .type = "ConfBridgeRec",
364 .description = "Conference Bridge Recording Channel",
365 .requester = rec_request,
369 static struct ast_channel *rec_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
371 struct ast_channel *tmp;
372 struct ast_format fmt;
373 const char *conf_name = data;
374 if (!(tmp = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", NULL, 0,
375 "ConfBridgeRecorder/conf-%s-uid-%d",
377 (int) ast_random()))) {
380 ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0);
381 ast_channel_tech_set(tmp, &record_tech);
382 ast_format_cap_add_all(ast_channel_nativeformats(tmp));
383 ast_format_copy(ast_channel_writeformat(tmp), &fmt);
384 ast_format_copy(ast_channel_rawwriteformat(tmp), &fmt);
385 ast_format_copy(ast_channel_readformat(tmp), &fmt);
386 ast_format_copy(ast_channel_rawreadformat(tmp), &fmt);
390 static void *record_thread(void *obj)
392 struct conference_bridge *conference_bridge = obj;
393 struct ast_app *mixmonapp = pbx_findapp("MixMonitor");
394 struct ast_channel *chan;
395 struct ast_str *filename = ast_str_alloca(PATH_MAX);
398 ao2_ref(conference_bridge, -1);
402 ao2_lock(conference_bridge);
403 if (!(conference_bridge->record_chan)) {
404 conference_bridge->record_thread = AST_PTHREADT_NULL;
405 ao2_unlock(conference_bridge);
406 ao2_ref(conference_bridge, -1);
409 chan = ast_channel_ref(conference_bridge->record_chan);
411 if (!(ast_strlen_zero(conference_bridge->b_profile.rec_file))) {
412 ast_str_append(&filename, 0, "%s", conference_bridge->b_profile.rec_file);
416 ast_str_append(&filename, 0, "confbridge-%s-%u.wav",
417 conference_bridge->name,
420 ao2_unlock(conference_bridge);
423 pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
424 ast_bridge_join(conference_bridge->bridge, chan, NULL, NULL, NULL);
426 ao2_lock(conference_bridge);
427 conference_bridge->record_thread = AST_PTHREADT_NULL;
428 ao2_unlock(conference_bridge);
430 ast_hangup(chan); /* This will eat this threads reference to the channel as well */
431 ao2_ref(conference_bridge, -1);
437 * \brief Returns whether or not conference is being recorded.
438 * \retval 1, conference is recording.
439 * \retval 0, conference is NOT recording.
441 static int conf_is_recording(struct conference_bridge *conference_bridge)
444 ao2_lock(conference_bridge);
445 if (conference_bridge->record_chan || conference_bridge->record_thread != AST_PTHREADT_NULL) {
448 ao2_unlock(conference_bridge);
454 * \brief Stops the confbridge recording thread.
456 * \note do not call this function with any locks
458 static int conf_stop_record(struct conference_bridge *conference_bridge)
460 ao2_lock(conference_bridge);
462 if (conference_bridge->record_thread != AST_PTHREADT_NULL) {
463 struct ast_channel *chan = ast_channel_ref(conference_bridge->record_chan);
464 pthread_t thread = conference_bridge->record_thread;
465 ao2_unlock(conference_bridge);
467 ast_bridge_remove(conference_bridge->bridge, chan);
468 ast_queue_frame(chan, &ast_null_frame);
470 chan = ast_channel_unref(chan);
471 pthread_join(thread, NULL);
472 ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference_bridge->b_profile.name);
474 ao2_lock(conference_bridge);
477 /* this is the reference given to the channel during the channel alloc */
478 if (conference_bridge->record_chan) {
479 conference_bridge->record_chan = ast_channel_unref(conference_bridge->record_chan);
482 ao2_unlock(conference_bridge);
486 static int conf_start_record(struct conference_bridge *conference_bridge)
488 struct ast_format_cap *cap = ast_format_cap_alloc_nolock();
489 struct ast_format tmpfmt;
492 ao2_lock(conference_bridge);
493 if (conference_bridge->record_chan || conference_bridge->record_thread != AST_PTHREADT_NULL) {
494 ao2_unlock(conference_bridge);
495 return -1; /* already recording */
498 ao2_unlock(conference_bridge);
501 if (!pbx_findapp("MixMonitor")) {
502 ast_log(LOG_WARNING, "Can not record ConfBridge, MixMonitor app is not installed\n");
503 cap = ast_format_cap_destroy(cap);
504 ao2_unlock(conference_bridge);
507 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
508 if (!(conference_bridge->record_chan = ast_request("ConfBridgeRec", cap, NULL, conference_bridge->name, &cause))) {
509 cap = ast_format_cap_destroy(cap);
510 ao2_unlock(conference_bridge);
514 cap = ast_format_cap_destroy(cap);
515 ao2_ref(conference_bridge, +1); /* give the record thread a ref */
517 if (ast_pthread_create_background(&conference_bridge->record_thread, NULL, record_thread, conference_bridge)) {
518 ast_log(LOG_WARNING, "Failed to create recording channel for conference %s\n", conference_bridge->name);
520 ao2_unlock(conference_bridge);
521 ao2_ref(conference_bridge, -1); /* error so remove ref */
525 ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference_bridge->b_profile.name);
526 ao2_unlock(conference_bridge);
530 static void send_conf_start_event(const char *conf_name)
532 manager_event(EVENT_FLAG_CALL, "ConfbridgeStart", "Conference: %s\r\n", conf_name);
535 static void send_conf_end_event(const char *conf_name)
537 manager_event(EVENT_FLAG_CALL, "ConfbridgeEnd", "Conference: %s\r\n", conf_name);
540 static void send_join_event(struct ast_channel *chan, const char *conf_name)
542 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeJoin",
546 "CallerIDnum: %s\r\n"
547 "CallerIDname: %s\r\n",
548 ast_channel_name(chan),
549 ast_channel_uniqueid(chan),
551 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
552 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
556 static void send_leave_event(struct ast_channel *chan, const char *conf_name)
558 ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeLeave",
562 "CallerIDnum: %s\r\n"
563 "CallerIDname: %s\r\n",
564 ast_channel_name(chan),
565 ast_channel_uniqueid(chan),
567 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
568 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>")
573 * \brief Announce number of users in the conference bridge to the caller
575 * \param conference_bridge Conference bridge to peek at
576 * \param (OPTIONAL) conference_bridge_user Caller
578 * \note if caller is NULL, the announcment will be sent to all participants in the conference.
579 * \return Returns 0 on success, -1 if the user hung up
581 static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
583 const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference_bridge->b_profile.sounds);
584 const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference_bridge->b_profile.sounds);
585 const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference_bridge->b_profile.sounds);
587 if (conference_bridge->users == 1) {
588 /* Awww we are the only person in the conference bridge */
590 } else if (conference_bridge->users == 2) {
591 if (conference_bridge_user) {
592 /* Eep, there is one other person */
593 if (ast_stream_and_wait(conference_bridge_user->chan,
599 play_sound_file(conference_bridge, only_one);
602 /* Alas multiple others in here */
603 if (conference_bridge_user) {
604 if (ast_stream_and_wait(conference_bridge_user->chan,
609 if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", ast_channel_language(conference_bridge_user->chan), NULL)) {
612 if (ast_stream_and_wait(conference_bridge_user->chan,
617 } else if (ast_fileexists(there_are, NULL, NULL) && ast_fileexists(other_in_party, NULL, NULL)) {
618 play_sound_file(conference_bridge, there_are);
619 play_sound_number(conference_bridge, conference_bridge->users - 1);
620 play_sound_file(conference_bridge, other_in_party);
627 * \brief Play back an audio file to a channel
629 * \param conference_bridge Conference bridge they are in
630 * \param chan Channel to play audio prompt to
631 * \param file Prompt to play
633 * \return Returns 0 on success, -1 if the user hung up
635 * \note This function assumes that conference_bridge is locked
637 static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
640 ao2_unlock(conference_bridge);
641 res = ast_stream_and_wait(chan, file, "");
642 ao2_lock(conference_bridge);
646 static void handle_video_on_join(struct conference_bridge *conference_bridge, struct ast_channel *chan, int marked)
648 /* Right now, only marked users are automatically set as the single src of video.*/
653 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
655 struct conference_bridge_user *tmp_user = NULL;
656 ao2_lock(conference_bridge);
657 /* see if anyone is already the video src */
658 AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
659 if (tmp_user->chan == chan) {
662 if (ast_bridge_is_video_src(conference_bridge->bridge, tmp_user->chan)) {
667 ao2_unlock(conference_bridge);
669 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
671 } else if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
672 /* we joined and are video capable, we override anyone else that may have already been the video feed */
673 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, chan);
677 static void handle_video_on_exit(struct conference_bridge *conference_bridge, struct ast_channel *chan)
679 struct conference_bridge_user *tmp_user = NULL;
681 /* if this isn't a video source, nothing to update */
682 if (!ast_bridge_is_video_src(conference_bridge->bridge, chan)) {
686 ast_bridge_remove_video_src(conference_bridge->bridge, chan);
688 /* If in follow talker mode, make sure to restore this mode on the
689 * bridge when a source is removed. It is possible this channel was
690 * only set temporarily as a video source by an AMI or DTMF action. */
691 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
692 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
695 /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
696 if (!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
697 !ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
701 /* Make the next available marked user the video src. */
702 ao2_lock(conference_bridge);
703 AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
704 if (tmp_user->chan == chan) {
707 if (ast_test_flag(&tmp_user->u_profile, USER_OPT_MARKEDUSER)) {
708 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, tmp_user->chan);
712 ao2_unlock(conference_bridge);
716 * \brief Perform post-joining marked specific actions
718 * \param conference_bridge Conference bridge being joined
719 * \param conference_bridge_user Conference bridge user joining
721 * \return Returns 0 on success, -1 if the user hung up
723 static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
725 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
726 struct conference_bridge_user *other_conference_bridge_user = NULL;
728 /* If we are not the first user to join, then the users are already
729 * in the conference so we do not need to update them. */
730 if (conference_bridge->markedusers >= 2) {
734 /* Iterate through every participant stopping MOH on them if need be */
735 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
736 if (other_conference_bridge_user == conference_bridge_user) {
739 if (other_conference_bridge_user->playing_moh && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
740 other_conference_bridge_user->playing_moh = 0;
741 ast_moh_stop(other_conference_bridge_user->chan);
742 ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
746 /* Next play the audio file stating they are going to be placed into the conference */
747 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
748 if (play_prompt_to_channel(conference_bridge,
749 conference_bridge_user->chan,
750 conf_get_sound(CONF_SOUND_PLACE_IN_CONF, conference_bridge_user->b_profile.sounds))) {
751 /* user hungup while the sound was playing */
756 /* Finally iterate through and unmute them all */
757 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
758 if (other_conference_bridge_user == conference_bridge_user) {
761 /* only unmute them if they are not supposed to start muted */
762 if (!ast_test_flag(&other_conference_bridge_user->u_profile, USER_OPT_STARTMUTED)) {
763 other_conference_bridge_user->features.mute = 0;
767 /* If a marked user already exists in the conference bridge we can just bail out now */
768 if (conference_bridge->markedusers) {
771 /* Be sure we are muted so we can't talk to anybody else waiting */
772 conference_bridge_user->features.mute = 1;
773 /* If we have not been quieted play back that they are waiting for the leader */
774 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
775 if (play_prompt_to_channel(conference_bridge,
776 conference_bridge_user->chan,
777 conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, conference_bridge_user->b_profile.sounds))) {
778 /* user hungup while the sound was playing */
782 /* Start music on hold if needed */
783 /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
784 * allowing a marked user to enter while the prompt was playing
786 if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) {
787 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL);
788 conference_bridge_user->playing_moh = 1;
795 * \brief Perform post-joining non-marked specific actions
797 * \param conference_bridge Conference bridge being joined
798 * \param conference_bridge_user Conference bridge user joining
800 * \return Returns 0 on success, -1 if the user hung up
802 static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
804 /* Play back audio prompt and start MOH if need be if we are the first participant */
805 if (conference_bridge->users == 1) {
806 /* If audio prompts have not been quieted or this prompt quieted play it on out */
807 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
808 if (play_prompt_to_channel(conference_bridge,
809 conference_bridge_user->chan,
810 conf_get_sound(CONF_SOUND_ONLY_PERSON, conference_bridge_user->b_profile.sounds))) {
811 /* user hungup while the sound was playing */
815 /* If we need to start music on hold on the channel do so now */
816 /* We need to re-check the number of users in the conference bridge here because another conference bridge
817 * participant could have joined while the above prompt was playing for the first user.
819 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) {
820 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL);
821 conference_bridge_user->playing_moh = 1;
826 /* Announce number of users if need be */
827 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
828 ao2_unlock(conference_bridge);
829 if (announce_user_count(conference_bridge, conference_bridge_user)) {
830 ao2_lock(conference_bridge);
833 ao2_lock(conference_bridge);
836 /* If we are the second participant we may need to stop music on hold on the first */
837 if (conference_bridge->users == 2) {
838 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
840 /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */
841 if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
842 first_participant->playing_moh = 0;
843 ast_moh_stop(first_participant->chan);
844 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
848 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
849 (conference_bridge->users > conference_bridge_user->u_profile.announce_user_count_all_after)) {
850 ao2_unlock(conference_bridge);
851 if (announce_user_count(conference_bridge, NULL)) {
852 ao2_lock(conference_bridge);
855 ao2_lock(conference_bridge);
861 * \brief Destroy a conference bridge
863 * \param obj The conference bridge object
865 * \return Returns nothing
867 static void destroy_conference_bridge(void *obj)
869 struct conference_bridge *conference_bridge = obj;
871 ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
873 ast_mutex_destroy(&conference_bridge->playback_lock);
875 if (conference_bridge->playback_chan) {
876 struct ast_channel *underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
877 if (underlying_channel) {
878 ast_hangup(underlying_channel);
880 ast_hangup(conference_bridge->playback_chan);
881 conference_bridge->playback_chan = NULL;
884 /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
885 if (conference_bridge->bridge) {
886 ast_bridge_destroy(conference_bridge->bridge);
887 conference_bridge->bridge = NULL;
889 conf_bridge_profile_destroy(&conference_bridge->b_profile);
892 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
895 * \brief Join a conference bridge
897 * \param name The conference name
898 * \param conference_bridge_user Conference bridge user structure
900 * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
902 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
904 struct conference_bridge *conference_bridge = NULL;
905 struct conference_bridge tmp;
906 int start_record = 0;
907 int max_members_reached = 0;
909 ast_copy_string(tmp.name, name, sizeof(tmp.name));
911 /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
912 ao2_lock(conference_bridges);
914 ast_debug(1, "Trying to find conference bridge '%s'\n", name);
916 /* Attempt to find an existing conference bridge */
917 conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
919 if (conference_bridge && conference_bridge->b_profile.max_members) {
920 max_members_reached = conference_bridge->b_profile.max_members > conference_bridge->users ? 0 : 1;
923 /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
924 if (conference_bridge && (max_members_reached || conference_bridge->locked) && !ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN)) {
925 ao2_unlock(conference_bridges);
926 ao2_ref(conference_bridge, -1);
927 ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
928 ast_stream_and_wait(conference_bridge_user->chan,
929 conf_get_sound(CONF_SOUND_LOCKED, conference_bridge_user->b_profile.sounds),
934 /* If no conference bridge was found see if we can create one */
935 if (!conference_bridge) {
936 /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
937 if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
938 ao2_unlock(conference_bridges);
939 ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
943 /* Setup conference bridge parameters */
944 conference_bridge->record_thread = AST_PTHREADT_NULL;
945 ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
946 conf_bridge_profile_copy(&conference_bridge->b_profile, &conference_bridge_user->b_profile);
948 /* Create an actual bridge that will do the audio mixing */
949 if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX, 0))) {
950 ao2_ref(conference_bridge, -1);
951 conference_bridge = NULL;
952 ao2_unlock(conference_bridges);
953 ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
957 /* Set the internal sample rate on the bridge from the bridge profile */
958 ast_bridge_set_internal_sample_rate(conference_bridge->bridge, conference_bridge->b_profile.internal_sample_rate);
959 /* Set the internal mixing interval on the bridge from the bridge profile */
960 ast_bridge_set_mixing_interval(conference_bridge->bridge, conference_bridge->b_profile.mix_interval);
962 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
963 ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
966 /* Setup lock for playback channel */
967 ast_mutex_init(&conference_bridge->playback_lock);
969 /* Link it into the conference bridges container */
970 ao2_link(conference_bridges, conference_bridge);
973 send_conf_start_event(conference_bridge->name);
974 ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
977 ao2_unlock(conference_bridges);
979 /* Setup conference bridge user parameters */
980 conference_bridge_user->conference_bridge = conference_bridge;
982 ao2_lock(conference_bridge);
984 /* All good to go, add them in */
985 AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
987 /* Increment the users count on the bridge, but record it as it is going to need to be known right after this */
988 conference_bridge->users++;
990 /* If the caller is a marked user bump up the count */
991 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
992 conference_bridge->markedusers++;
995 /* Set the device state for this conference */
996 if (conference_bridge->users == 1) {
997 ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name);
1000 /* If an announcement is to be played play it */
1001 if (!ast_strlen_zero(conference_bridge_user->u_profile.announcement)) {
1002 if (play_prompt_to_channel(conference_bridge,
1003 conference_bridge_user->chan,
1004 conference_bridge_user->u_profile.announcement)) {
1005 ao2_unlock(conference_bridge);
1006 leave_conference_bridge(conference_bridge, conference_bridge_user);
1011 /* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
1012 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER | USER_OPT_WAITMARKED)) {
1013 if (post_join_marked(conference_bridge, conference_bridge_user)) {
1014 ao2_unlock(conference_bridge);
1015 leave_conference_bridge(conference_bridge, conference_bridge_user);
1019 if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
1020 ao2_unlock(conference_bridge);
1021 leave_conference_bridge(conference_bridge, conference_bridge_user);
1026 /* check to see if recording needs to be started or not */
1027 if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_RECORD_CONFERENCE) && !conf_is_recording(conference_bridge)) {
1031 ao2_unlock(conference_bridge);
1034 conf_start_record(conference_bridge);
1037 return conference_bridge;
1041 * \brief Leave a conference bridge
1043 * \param conference_bridge The conference bridge to leave
1044 * \param conference_bridge_user The conference bridge user structure
1047 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
1049 ao2_lock(conference_bridge);
1051 /* If this caller is a marked user bump down the count */
1052 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
1053 conference_bridge->markedusers--;
1056 /* Decrement the users count while keeping the previous participant count */
1057 conference_bridge->users--;
1059 /* Drop conference bridge user from the list, they be going bye bye */
1060 AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
1062 /* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */
1063 if (conference_bridge->users) {
1064 if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER) && !conference_bridge->markedusers) {
1065 struct conference_bridge_user *other_participant = NULL;
1067 /* Start out with muting everyone */
1068 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
1069 other_participant->features.mute = 1;
1072 /* Play back the audio prompt saying the leader has left the conference */
1073 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
1074 ao2_unlock(conference_bridge);
1075 ast_autoservice_start(conference_bridge_user->chan);
1076 play_sound_file(conference_bridge,
1077 conf_get_sound(CONF_SOUND_LEADER_HAS_LEFT, conference_bridge_user->b_profile.sounds));
1078 ast_autoservice_stop(conference_bridge_user->chan);
1079 ao2_lock(conference_bridge);
1082 /* Now on to starting MOH or kick if needed */
1083 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
1084 if (ast_test_flag(&other_participant->u_profile, USER_OPT_ENDMARKED)) {
1085 other_participant->kicked = 1;
1086 ast_bridge_remove(conference_bridge->bridge, other_participant->chan);
1087 } else if (ast_test_flag(&other_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
1088 ast_moh_start(other_participant->chan, other_participant->u_profile.moh_class, NULL);
1089 other_participant->playing_moh = 1;
1090 ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
1093 } else if (conference_bridge->users == 1) {
1094 /* Of course if there is one other person in here we may need to start up MOH on them */
1095 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
1097 if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
1098 ast_moh_start(first_participant->chan, first_participant->u_profile.moh_class, NULL);
1099 first_participant->playing_moh = 1;
1100 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
1104 /* Set device state to "not in use" */
1105 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", conference_bridge->name);
1107 ao2_unlink(conference_bridges, conference_bridge);
1108 send_conf_end_event(conference_bridge->name);
1111 /* Done mucking with the conference bridge, huzzah */
1112 ao2_unlock(conference_bridge);
1114 if (!conference_bridge->users) {
1115 conf_stop_record(conference_bridge);
1118 ao2_ref(conference_bridge, -1);
1123 * \brief allocates playback chan on a channel
1124 * \pre expects conference to be locked before calling this function
1126 static int alloc_playback_chan(struct conference_bridge *conference_bridge)
1129 struct ast_format_cap *cap;
1130 struct ast_format tmpfmt;
1132 if (conference_bridge->playback_chan) {
1135 if (!(cap = ast_format_cap_alloc_nolock())) {
1138 ast_format_cap_add(cap, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
1139 if (!(conference_bridge->playback_chan = ast_request("Bridge", cap, NULL, "", &cause))) {
1140 cap = ast_format_cap_destroy(cap);
1143 cap = ast_format_cap_destroy(cap);
1145 ast_channel_internal_bridge_set(conference_bridge->playback_chan, conference_bridge->bridge);
1147 if (ast_call(conference_bridge->playback_chan, "", 0)) {
1148 ast_hangup(conference_bridge->playback_chan);
1149 conference_bridge->playback_chan = NULL;
1153 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
1157 static int play_sound_helper(struct conference_bridge *conference_bridge, const char *filename, int say_number)
1159 struct ast_channel *underlying_channel;
1161 /* Do not waste resources trying to play files that do not exist */
1162 if (!ast_fileexists(filename, NULL, NULL)) {
1163 ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
1167 ast_mutex_lock(&conference_bridge->playback_lock);
1168 if (!(conference_bridge->playback_chan)) {
1169 if (alloc_playback_chan(conference_bridge)) {
1170 ast_mutex_unlock(&conference_bridge->playback_lock);
1173 underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
1175 /* Channel was already available so we just need to add it back into the bridge */
1176 underlying_channel = ast_channel_tech(conference_bridge->playback_chan)->bridged_channel(conference_bridge->playback_chan, NULL);
1177 ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL, 0);
1180 /* The channel is all under our control, in goes the prompt */
1181 if (!ast_strlen_zero(filename)) {
1182 ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
1184 ast_say_number(conference_bridge->playback_chan, say_number, "", ast_channel_language(conference_bridge->playback_chan), NULL);
1187 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", ast_channel_name(underlying_channel), conference_bridge->bridge);
1188 ast_bridge_depart(conference_bridge->bridge, underlying_channel);
1190 ast_mutex_unlock(&conference_bridge->playback_lock);
1196 * \brief Play sound file into conference bridge
1198 * \param conference_bridge The conference bridge to play sound file into
1199 * \param filename Sound file to play
1202 * \retval -1 failure
1204 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
1206 return play_sound_helper(conference_bridge, filename, 0);
1210 * \brief Play number into the conference bridge
1212 * \param conference_bridge The conference bridge to say the number into
1213 * \param number to say
1216 * \retval -1 failure
1218 static int play_sound_number(struct conference_bridge *conference_bridge, int say_number)
1220 return play_sound_helper(conference_bridge, NULL, say_number);
1223 static void conf_handle_talker_destructor(void *pvt_data)
1228 static void conf_handle_talker_cb(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *pvt_data)
1230 char *conf_name = pvt_data;
1233 switch (bridge_channel->state) {
1234 case AST_BRIDGE_CHANNEL_STATE_START_TALKING:
1237 case AST_BRIDGE_CHANNEL_STATE_STOP_TALKING:
1241 return; /* uhh this shouldn't happen, but bail if it does. */
1244 /* notify AMI someone is has either started or stopped talking */
1245 ast_manager_event(bridge_channel->chan, EVENT_FLAG_CALL, "ConfbridgeTalking",
1248 "Conference: %s\r\n"
1249 "TalkingStatus: %s\r\n",
1250 ast_channel_name(bridge_channel->chan), ast_channel_uniqueid(bridge_channel->chan), conf_name, talking ? "on" : "off");
1253 static int conf_get_pin(struct ast_channel *chan, struct conference_bridge_user *conference_bridge_user)
1255 char pin_guess[MAX_PIN+1] = { 0, };
1256 const char *pin = conference_bridge_user->u_profile.pin;
1257 char *tmp = pin_guess;
1259 unsigned int len = MAX_PIN ;
1261 /* give them three tries to get the pin right */
1262 for (i = 0; i < 3; i++) {
1263 if (ast_app_getdata(chan,
1264 conf_get_sound(CONF_SOUND_GET_PIN, conference_bridge_user->b_profile.sounds),
1265 tmp, len, 0) >= 0) {
1266 if (!strcasecmp(pin, pin_guess)) {
1270 ast_streamfile(chan,
1271 conf_get_sound(CONF_SOUND_INVALID_PIN, conference_bridge_user->b_profile.sounds),
1272 ast_channel_language(chan));
1273 res = ast_waitstream(chan, AST_DIGIT_ANY);
1275 /* Account for digit already read during ivalid pin playback
1276 * resetting pin buf. */
1278 pin_guess[1] = '\0';
1279 tmp = pin_guess + 1;
1282 /* reset pin buf as empty buffer. */
1290 static int conf_rec_name(struct conference_bridge_user *user, const char *conf_name)
1292 char destdir[PATH_MAX];
1296 snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
1298 if (ast_mkdir(destdir, 0777) != 0) {
1299 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1302 snprintf(user->name_rec_location, sizeof(user->name_rec_location),
1303 "%s/confbridge-name-%s-%s", destdir,
1304 conf_name, ast_channel_uniqueid(user->chan));
1306 res = ast_play_and_record(user->chan,
1308 user->name_rec_location,
1313 ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE),
1318 user->name_rec_location[0] = '\0';
1324 /*! \brief The ConfBridge application */
1325 static int confbridge_exec(struct ast_channel *chan, const char *data)
1327 int res = 0, volume_adjustments[2];
1330 const char *b_profile_name = DEFAULT_BRIDGE_PROFILE;
1331 const char *u_profile_name = DEFAULT_USER_PROFILE;
1332 struct conference_bridge *conference_bridge = NULL;
1333 struct conference_bridge_user conference_bridge_user = {
1335 .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
1336 .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
1337 .tech_args.drop_silence = 0,
1339 AST_DECLARE_APP_ARGS(args,
1340 AST_APP_ARG(conf_name);
1341 AST_APP_ARG(b_profile_name);
1342 AST_APP_ARG(u_profile_name);
1343 AST_APP_ARG(menu_name);
1345 ast_bridge_features_init(&conference_bridge_user.features);
1347 if (ast_channel_state(chan) != AST_STATE_UP) {
1351 if (ast_strlen_zero(data)) {
1352 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
1353 res = -1; /* invalid PIN */
1354 goto confbridge_cleanup;
1357 /* We need to make a copy of the input string if we are going to modify it! */
1358 parse = ast_strdupa(data);
1360 AST_STANDARD_APP_ARGS(args, parse);
1362 /* bridge profile name */
1363 if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
1364 b_profile_name = args.b_profile_name;
1366 if (!conf_find_bridge_profile(chan, b_profile_name, &conference_bridge_user.b_profile)) {
1367 ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name);
1369 goto confbridge_cleanup;
1372 /* user profile name */
1373 if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
1374 u_profile_name = args.u_profile_name;
1377 if (!conf_find_user_profile(chan, u_profile_name, &conference_bridge_user.u_profile)) {
1378 ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name);
1380 goto confbridge_cleanup;
1382 quiet = ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_QUIET);
1384 /* ask for a PIN immediately after finding user profile. This has to be
1385 * prompted for requardless of quiet setting. */
1386 if (!ast_strlen_zero(conference_bridge_user.u_profile.pin)) {
1387 if (conf_get_pin(chan, &conference_bridge_user)) {
1388 res = -1; /* invalid PIN */
1389 goto confbridge_cleanup;
1393 /* See if we need them to record a intro name */
1394 if (!quiet && ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE)) {
1395 conf_rec_name(&conference_bridge_user, args.conf_name);
1399 if (args.argc > 3 && !ast_strlen_zero(args.menu_name)) {
1400 ast_copy_string(conference_bridge_user.menu_name, args.menu_name, sizeof(conference_bridge_user.menu_name));
1401 if (conf_set_menu_to_user(conference_bridge_user.menu_name, &conference_bridge_user)) {
1402 ast_log(LOG_WARNING, "Conference menu %s does not exist and can not be applied to confbridge user.\n",
1404 res = -1; /* invalid PIN */
1405 goto confbridge_cleanup;
1409 /* Set if DTMF should pass through for this user or not */
1410 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DTMF_PASS)) {
1411 conference_bridge_user.features.dtmf_passthrough = 1;
1414 /* Set dsp threshold values if present */
1415 if (conference_bridge_user.u_profile.talking_threshold) {
1416 conference_bridge_user.tech_args.talking_threshold = conference_bridge_user.u_profile.talking_threshold;
1418 if (conference_bridge_user.u_profile.silence_threshold) {
1419 conference_bridge_user.tech_args.silence_threshold = conference_bridge_user.u_profile.silence_threshold;
1422 /* Set a talker indicate call back if talking detection is requested */
1423 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_TALKER_DETECT)) {
1424 char *conf_name = ast_strdup(args.conf_name); /* this is freed during feature cleanup */
1426 res = -1; /* invalid PIN */
1427 goto confbridge_cleanup;
1429 ast_bridge_features_set_talk_detector(&conference_bridge_user.features,
1430 conf_handle_talker_cb,
1431 conf_handle_talker_destructor,
1435 /* Look for a conference bridge matching the provided name */
1436 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
1437 res = -1; /* invalid PIN */
1438 goto confbridge_cleanup;
1441 /* Keep a copy of volume adjustments so we can restore them later if need be */
1442 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
1443 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
1445 /* If the caller should be joined already muted, make it so */
1446 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_STARTMUTED)) {
1447 conference_bridge_user.features.mute = 1;
1450 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DROP_SILENCE)) {
1451 conference_bridge_user.tech_args.drop_silence = 1;
1454 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_JITTERBUFFER)) {
1456 if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) {
1458 ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
1462 if (ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_DENOISE)) {
1464 /* Reduce background noise from each participant */
1465 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
1466 ast_free(mod_speex);
1467 ast_func_write(chan, "DENOISE(rx)", "on");
1471 /* if this user has a intro, play it before entering */
1472 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1473 ast_autoservice_start(chan);
1474 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1475 play_sound_file(conference_bridge,
1476 conf_get_sound(CONF_SOUND_HAS_JOINED, conference_bridge_user.b_profile.sounds));
1477 ast_autoservice_stop(chan);
1480 /* Play the Join sound to both the conference and the user entering. */
1482 const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, conference_bridge_user.b_profile.sounds);
1483 if (conference_bridge_user.playing_moh) {
1486 ast_stream_and_wait(chan, join_sound, "");
1487 ast_autoservice_start(chan);
1488 play_sound_file(conference_bridge, join_sound);
1489 ast_autoservice_stop(chan);
1490 if (conference_bridge_user.playing_moh) {
1491 ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL);
1495 /* See if we need to automatically set this user as a video source or not */
1496 handle_video_on_join(conference_bridge, conference_bridge_user.chan, ast_test_flag(&conference_bridge_user.u_profile, USER_OPT_MARKEDUSER));
1498 /* Join our conference bridge for real */
1499 send_join_event(conference_bridge_user.chan, conference_bridge->name);
1500 ast_bridge_join(conference_bridge->bridge,
1503 &conference_bridge_user.features,
1504 &conference_bridge_user.tech_args);
1505 send_leave_event(conference_bridge_user.chan, conference_bridge->name);
1507 /* if we're shutting down, don't attempt to do further processing */
1508 if (ast_shutting_down()) {
1509 leave_conference_bridge(conference_bridge, &conference_bridge_user);
1510 conference_bridge = NULL;
1511 goto confbridge_cleanup;
1514 /* If this user was a video source, we need to clean up and possibly pick a new source. */
1515 handle_video_on_exit(conference_bridge, conference_bridge_user.chan);
1517 /* if this user has a intro, play it when leaving */
1518 if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1519 ast_autoservice_start(chan);
1520 play_sound_file(conference_bridge, conference_bridge_user.name_rec_location);
1521 play_sound_file(conference_bridge,
1522 conf_get_sound(CONF_SOUND_HAS_LEFT, conference_bridge_user.b_profile.sounds));
1523 ast_autoservice_stop(chan);
1526 /* play the leave sound */
1528 const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, conference_bridge_user.b_profile.sounds);
1529 ast_autoservice_start(chan);
1530 play_sound_file(conference_bridge, leave_sound);
1531 ast_autoservice_stop(chan);
1534 /* Easy as pie, depart this channel from the conference bridge */
1535 leave_conference_bridge(conference_bridge, &conference_bridge_user);
1536 conference_bridge = NULL;
1538 /* If the user was kicked from the conference play back the audio prompt for it */
1539 if (!quiet && conference_bridge_user.kicked) {
1540 res = ast_stream_and_wait(chan,
1541 conf_get_sound(CONF_SOUND_KICKED, conference_bridge_user.b_profile.sounds),
1545 /* Restore volume adjustments to previous values in case they were changed */
1546 if (volume_adjustments[0]) {
1547 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
1549 if (volume_adjustments[1]) {
1550 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
1553 if (!ast_strlen_zero(conference_bridge_user.name_rec_location)) {
1554 ast_filedelete(conference_bridge_user.name_rec_location, NULL);
1558 ast_bridge_features_cleanup(&conference_bridge_user.features);
1559 conf_bridge_profile_destroy(&conference_bridge_user.b_profile);
1563 static int action_toggle_mute(struct conference_bridge *conference_bridge,
1564 struct conference_bridge_user *conference_bridge_user,
1565 struct ast_channel *chan)
1567 /* Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
1568 if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_WAITMARKED) || conference_bridge->markedusers) {
1569 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
1570 ast_test_suite_event_notify("CONF_MUTE", "Message: participant %s %s\r\nConference: %s\r\nChannel: %s", ast_channel_name(chan), conference_bridge_user->features.mute ? "muted" : "unmuted", conference_bridge_user->b_profile.name, ast_channel_name(chan));
1572 return ast_stream_and_wait(chan, (conference_bridge_user->features.mute ?
1573 conf_get_sound(CONF_SOUND_MUTED, conference_bridge_user->b_profile.sounds) :
1574 conf_get_sound(CONF_SOUND_UNMUTED, conference_bridge_user->b_profile.sounds)),
1578 static int action_toggle_mute_participants(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
1580 struct conference_bridge_user *participant = NULL;
1581 const char *sound_to_play;
1583 ao2_lock(conference_bridge);
1585 /* If already muted, then unmute */
1586 conference_bridge->muted = conference_bridge->muted ? 0 : 1;
1587 sound_to_play = conf_get_sound((conference_bridge->muted ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED),
1588 conference_bridge_user->b_profile.sounds);
1590 AST_LIST_TRAVERSE(&conference_bridge->users_list, participant, list) {
1591 if (!ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
1592 participant->features.mute = conference_bridge->muted;
1596 ao2_unlock(conference_bridge);
1598 /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
1599 ast_stream_and_wait(conference_bridge_user->chan, sound_to_play, "");
1601 /* Announce to the group that all participants are muted */
1602 ast_autoservice_start(conference_bridge_user->chan);
1603 play_sound_helper(conference_bridge, sound_to_play, 0);
1604 ast_autoservice_stop(conference_bridge_user->chan);
1609 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
1611 char *file_copy = ast_strdupa(playback_file);
1614 while ((file = strsep(&file_copy, "&"))) {
1615 if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
1616 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1623 static int action_playback_and_continue(struct conference_bridge *conference_bridge,
1624 struct conference_bridge_user *conference_bridge_user,
1625 struct ast_bridge_channel *bridge_channel,
1626 struct conf_menu *menu,
1627 const char *playback_file,
1628 const char *cur_dtmf,
1633 char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
1634 struct conf_menu_entry new_menu_entry = { { 0, }, };
1635 char *file_copy = ast_strdupa(playback_file);
1638 while ((file = strsep(&file_copy, "&"))) {
1639 if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
1640 ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1644 /* now wait for more digits. */
1645 if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
1646 /* streaming finished and no DTMF was entered */
1648 } else if (digit == -1) {
1652 break; /* dtmf was entered */
1656 /* streaming finished on all files and no DTMF was entered */
1659 ast_stopstream(bridge_channel->chan);
1661 /* If we get here, then DTMF has been entered, This means no
1662 * additional prompts should be played for this menu entry */
1665 /* If a digit was pressed during the payback, update
1666 * the dtmf string and look for a new menu entry in the
1668 ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
1669 for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
1670 dtmf[i] = cur_dtmf[i];
1672 dtmf[i] = (char) digit;
1678 /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
1679 * If this is the case, no new DTMF sequence should be looked for. */
1684 if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
1685 execute_menu_entry(conference_bridge,
1686 conference_bridge_user,
1688 &new_menu_entry, menu);
1689 conf_menu_entry_destroy(&new_menu_entry);
1694 static int action_kick_last(struct conference_bridge *conference_bridge,
1695 struct ast_bridge_channel *bridge_channel,
1696 struct conference_bridge_user *conference_bridge_user)
1698 struct conference_bridge_user *last_participant = NULL;
1699 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
1702 ast_stream_and_wait(bridge_channel->chan,
1703 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1705 ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
1706 ast_channel_name(bridge_channel->chan),
1707 conference_bridge->name);
1711 ao2_lock(conference_bridge);
1712 if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user)
1713 || (ast_test_flag(&last_participant->u_profile, USER_OPT_ADMIN))) {
1714 ao2_unlock(conference_bridge);
1715 ast_stream_and_wait(bridge_channel->chan,
1716 conf_get_sound(CONF_SOUND_ERROR_MENU, conference_bridge_user->b_profile.sounds),
1718 } else if (last_participant) {
1719 last_participant->kicked = 1;
1720 ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
1721 ao2_unlock(conference_bridge);
1726 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
1728 struct ast_pbx_args args;
1729 struct ast_pbx *pbx;
1735 memset(&args, 0, sizeof(args));
1736 args.no_hangup_chan = 1;
1738 ast_channel_lock(bridge_channel->chan);
1741 exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
1742 context = ast_strdupa(ast_channel_context(bridge_channel->chan));
1743 priority = ast_channel_priority(bridge_channel->chan);
1744 pbx = ast_channel_pbx(bridge_channel->chan);
1745 ast_channel_pbx_set(bridge_channel->chan, NULL);
1748 ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
1749 ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
1750 ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
1752 ast_channel_unlock(bridge_channel->chan);
1755 res = ast_pbx_run_args(bridge_channel->chan, &args);
1758 ast_channel_lock(bridge_channel->chan);
1760 ast_channel_exten_set(bridge_channel->chan, exten);
1761 ast_channel_context_set(bridge_channel->chan, context);
1762 ast_channel_priority_set(bridge_channel->chan, priority);
1763 ast_channel_pbx_set(bridge_channel->chan, pbx);
1765 ast_channel_unlock(bridge_channel->chan);
1770 static int execute_menu_entry(struct conference_bridge *conference_bridge,
1771 struct conference_bridge_user *conference_bridge_user,
1772 struct ast_bridge_channel *bridge_channel,
1773 struct conf_menu_entry *menu_entry,
1774 struct conf_menu *menu)
1776 struct conf_menu_action *menu_action;
1777 int isadmin = ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ADMIN);
1778 int stop_prompts = 0;
1781 AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
1782 switch (menu_action->id) {
1783 case MENU_ACTION_TOGGLE_MUTE:
1784 res |= action_toggle_mute(conference_bridge,
1785 conference_bridge_user,
1786 bridge_channel->chan);
1788 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
1792 action_toggle_mute_participants(conference_bridge, conference_bridge_user);
1794 case MENU_ACTION_PARTICIPANT_COUNT:
1795 announce_user_count(conference_bridge, conference_bridge_user);
1797 case MENU_ACTION_PLAYBACK:
1798 if (!stop_prompts) {
1799 res |= action_playback(bridge_channel, menu_action->data.playback_file);
1802 case MENU_ACTION_RESET_LISTENING:
1803 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
1805 case MENU_ACTION_RESET_TALKING:
1806 ast_audiohook_volume_set(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
1808 case MENU_ACTION_INCREASE_LISTENING:
1809 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1810 AST_AUDIOHOOK_DIRECTION_WRITE, 1);
1812 case MENU_ACTION_DECREASE_LISTENING:
1813 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1814 AST_AUDIOHOOK_DIRECTION_WRITE, -1);
1816 case MENU_ACTION_INCREASE_TALKING:
1817 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1818 AST_AUDIOHOOK_DIRECTION_READ, 1);
1820 case MENU_ACTION_DECREASE_TALKING:
1821 ast_audiohook_volume_adjust(conference_bridge_user->chan,
1822 AST_AUDIOHOOK_DIRECTION_READ, -1);
1824 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
1825 if (!(stop_prompts)) {
1826 res |= action_playback_and_continue(conference_bridge,
1827 conference_bridge_user,
1830 menu_action->data.playback_file,
1835 case MENU_ACTION_DIALPLAN_EXEC:
1836 res |= action_dialplan_exec(bridge_channel, menu_action);
1838 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
1842 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
1843 res |= ast_stream_and_wait(bridge_channel->chan,
1844 (conference_bridge->locked ?
1845 conf_get_sound(CONF_SOUND_LOCKED_NOW, conference_bridge_user->b_profile.sounds) :
1846 conf_get_sound(CONF_SOUND_UNLOCKED_NOW, conference_bridge_user->b_profile.sounds)),
1850 case MENU_ACTION_ADMIN_KICK_LAST:
1851 res |= action_kick_last(conference_bridge, bridge_channel, conference_bridge_user);
1853 case MENU_ACTION_LEAVE:
1854 ao2_lock(conference_bridge);
1855 ast_bridge_remove(conference_bridge->bridge, bridge_channel->chan);
1856 ao2_unlock(conference_bridge);
1858 case MENU_ACTION_NOOP:
1860 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
1861 ao2_lock(conference_bridge);
1862 ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
1863 ao2_unlock(conference_bridge);
1865 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
1866 handle_video_on_exit(conference_bridge, bridge_channel->chan);
1873 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
1874 struct conference_bridge_user *conference_bridge_user,
1875 struct conf_menu_entry *menu_entry,
1876 struct conf_menu *menu)
1878 struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
1880 /* See if music on hold is playing */
1881 ao2_lock(conference_bridge);
1882 if (conference_bridge_user->playing_moh) {
1883 /* MOH is going, let's stop it */
1884 ast_moh_stop(bridge_channel->chan);
1886 ao2_unlock(conference_bridge);
1888 /* execute the list of actions associated with this menu entry */
1889 execute_menu_entry(conference_bridge, conference_bridge_user, bridge_channel, menu_entry, menu);
1891 /* See if music on hold needs to be started back up again */
1892 ao2_lock(conference_bridge);
1893 if (conference_bridge_user->playing_moh) {
1894 ast_moh_start(bridge_channel->chan, conference_bridge_user->u_profile.moh_class, NULL);
1896 ao2_unlock(conference_bridge);
1901 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
1904 struct conference_bridge *bridge = NULL;
1906 int wordlen = strlen(word);
1907 struct ao2_iterator i;
1909 i = ao2_iterator_init(conference_bridges, 0);
1910 while ((bridge = ao2_iterator_next(&i))) {
1911 if (!strncasecmp(bridge->name, word, wordlen) && ++which > state) {
1912 res = ast_strdup(bridge->name);
1913 ao2_ref(bridge, -1);
1916 ao2_ref(bridge, -1);
1918 ao2_iterator_destroy(&i);
1923 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1925 struct conference_bridge *bridge = NULL;
1926 struct conference_bridge tmp;
1927 struct conference_bridge_user *participant = NULL;
1931 e->command = "confbridge kick";
1933 "Usage: confbridge kick <conference> <channel>\n"
1934 " Kicks a channel out of the conference bridge.\n";
1938 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
1942 return complete_confbridge_channel(a->line, a->word, a->pos, a->n);
1949 return CLI_SHOWUSAGE;
1952 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
1953 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
1955 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
1959 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
1960 if (!strncmp(a->argv[3], ast_channel_name(participant->chan), strlen(ast_channel_name(participant->chan)))) {
1965 ast_cli(a->fd, "Kicking %s from confbridge %s\n", ast_channel_name(participant->chan), bridge->name);
1966 participant->kicked = 1;
1967 ast_bridge_remove(bridge->bridge, participant->chan);
1970 ao2_ref(bridge, -1);
1974 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1976 struct ao2_iterator i;
1977 struct conference_bridge *bridge = NULL;
1978 struct conference_bridge tmp;
1979 struct conference_bridge_user *participant = NULL;
1983 e->command = "confbridge list";
1985 "Usage: confbridge list [<name>]\n"
1986 " Lists all currently active conference bridges.\n";
1990 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
1996 ast_cli(a->fd, "Conference Bridge Name Users Marked Locked?\n");
1997 ast_cli(a->fd, "================================ ====== ====== ========\n");
1998 i = ao2_iterator_init(conference_bridges, 0);
1999 while ((bridge = ao2_iterator_next(&i))) {
2000 ast_cli(a->fd, "%-32s %6i %6i %s\n", bridge->name, bridge->users, bridge->markedusers, (bridge->locked ? "locked" : "unlocked"));
2001 ao2_ref(bridge, -1);
2003 ao2_iterator_destroy(&i);
2008 ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
2009 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2011 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2014 ast_cli(a->fd, "Channel User Profile Bridge Profile Menu CallerID\n");
2015 ast_cli(a->fd, "============================= ================ ================ ================ ================\n");
2017 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2018 ast_cli(a->fd, "%-29s ", ast_channel_name(participant->chan));
2019 ast_cli(a->fd, "%-17s", participant->u_profile.name);
2020 ast_cli(a->fd, "%-17s", participant->b_profile.name);
2021 ast_cli(a->fd, "%-17s", participant->menu_name);
2022 ast_cli(a->fd, "%-17s", S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"));
2023 ast_cli(a->fd, "\n");
2026 ao2_ref(bridge, -1);
2030 return CLI_SHOWUSAGE;
2034 * \brief finds a conference by name and locks/unlocks.
2037 * \retval -1 conference not found
2039 static int generic_lock_unlock_helper(int lock, const char *conference)
2041 struct conference_bridge *bridge = NULL;
2042 struct conference_bridge tmp;
2045 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2046 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2051 bridge->locked = lock;
2052 ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", bridge->locked ? "locked" : "unlocked", bridge->b_profile.name);
2054 ao2_ref(bridge, -1);
2060 * \brief finds a conference user by channel name and mutes/unmutes them.
2063 * \retval -1 conference not found
2064 * \retval -2 user not found
2066 static int generic_mute_unmute_helper(int mute, const char *conference, const char *user)
2068 struct conference_bridge *bridge = NULL;
2069 struct conference_bridge tmp;
2070 struct conference_bridge_user *participant = NULL;
2072 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2073 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2078 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2079 if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) {
2084 participant->features.mute = mute;
2085 ast_test_suite_event_notify("CONF_MUTE", "Message: participant %s %s\r\nConference: %s\r\nChannel: %s", ast_channel_name(participant->chan), participant->features.mute ? "muted" : "unmuted", bridge->b_profile.name, ast_channel_name(participant->chan));
2090 ao2_ref(bridge, -1);
2095 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
2097 int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
2100 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2102 } else if (res == -2) {
2103 ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
2106 ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
2110 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2114 e->command = "confbridge mute";
2116 "Usage: confbridge mute <conference> <channel>\n";
2120 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2125 return CLI_SHOWUSAGE;
2128 cli_mute_unmute_helper(1, a);
2133 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2137 e->command = "confbridge unmute";
2139 "Usage: confbridge unmute <conference> <channel>\n";
2143 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2148 return CLI_SHOWUSAGE;
2151 cli_mute_unmute_helper(0, a);
2156 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2160 e->command = "confbridge lock";
2162 "Usage: confbridge lock <conference>\n";
2166 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2171 return CLI_SHOWUSAGE;
2173 if (generic_lock_unlock_helper(1, a->argv[2])) {
2174 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2176 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
2181 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2185 e->command = "confbridge unlock";
2187 "Usage: confbridge unlock <conference>\n";
2191 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2196 return CLI_SHOWUSAGE;
2198 if (generic_lock_unlock_helper(0, a->argv[2])) {
2199 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2201 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
2206 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2208 const char *rec_file = NULL;
2209 struct conference_bridge *bridge = NULL;
2210 struct conference_bridge tmp;
2214 e->command = "confbridge record start";
2216 "Usage: confbridge record start <conference> <file>\n"
2217 " <file> is optional, Otherwise the bridge profile\n"
2218 " record file will be used. If the bridge profile\n"
2219 " has no record file specified, a file will automatically\n"
2220 " be generated in the monitor directory\n";
2224 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2229 return CLI_SHOWUSAGE;
2232 rec_file = a->argv[4];
2235 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2236 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2238 ast_cli(a->fd, "Conference not found.\n");
2241 if (conf_is_recording(bridge)) {
2242 ast_cli(a->fd, "Conference is already being recorded.\n");
2243 ao2_ref(bridge, -1);
2246 if (!ast_strlen_zero(rec_file)) {
2248 ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file));
2251 if (conf_start_record(bridge)) {
2252 ast_cli(a->fd, "Could not start recording due to internal error.\n");
2253 ao2_ref(bridge, -1);
2256 ast_cli(a->fd, "Recording started\n");
2257 ao2_ref(bridge, -1);
2261 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2263 struct conference_bridge *bridge = NULL;
2264 struct conference_bridge tmp;
2268 e->command = "confbridge record stop";
2270 "Usage: confbridge record stop <conference>\n";
2274 return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2279 return CLI_SHOWUSAGE;
2282 ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
2283 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2285 ast_cli(a->fd, "Conference not found.\n");
2288 conf_stop_record(bridge);
2289 ast_cli(a->fd, "Recording stopped.\n");
2290 ao2_ref(bridge, -1);
2294 static struct ast_cli_entry cli_confbridge[] = {
2295 AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
2296 AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
2297 AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute a participant."),
2298 AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute a participant."),
2299 AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
2300 AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
2301 AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
2302 AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
2304 static struct ast_custom_function confbridge_function = {
2305 .name = "CONFBRIDGE",
2306 .write = func_confbridge_helper,
2309 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
2310 static struct ast_custom_function confbridge_info_function = {
2311 .name = "CONFBRIDGE_INFO",
2312 .read = func_confbridge_info,
2315 static int action_confbridgelist(struct mansession *s, const struct message *m)
2317 const char *actionid = astman_get_header(m, "ActionID");
2318 const char *conference = astman_get_header(m, "Conference");
2319 struct conference_bridge_user *participant = NULL;
2320 struct conference_bridge *bridge = NULL;
2321 struct conference_bridge tmp;
2322 char id_text[80] = "";
2325 if (!ast_strlen_zero(actionid)) {
2326 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2328 if (ast_strlen_zero(conference)) {
2329 astman_send_error(s, m, "No Conference name provided.");
2332 if (!ao2_container_count(conference_bridges)) {
2333 astman_send_error(s, m, "No active conferences.");
2336 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2337 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2339 astman_send_error(s, m, "No Conference by that name found.");
2343 astman_send_listack(s, m, "Confbridge user list will follow", "start");
2346 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2349 "Event: ConfbridgeList\r\n"
2351 "Conference: %s\r\n"
2352 "CallerIDNum: %s\r\n"
2353 "CallerIDName: %s\r\n"
2356 "MarkedUser: %s\r\n"
2360 S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"),
2361 S_COR(ast_channel_caller(participant->chan)->id.name.valid, ast_channel_caller(participant->chan)->id.name.str, "<no name>"),
2362 ast_channel_name(participant->chan),
2363 ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
2364 ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No");
2367 ao2_ref(bridge, -1);
2370 "Event: ConfbridgeListComplete\r\n"
2371 "EventList: Complete\r\n"
2374 "\r\n", total, id_text);
2379 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
2381 const char *actionid = astman_get_header(m, "ActionID");
2382 struct conference_bridge *bridge = NULL;
2383 struct ao2_iterator i;
2384 char id_text[512] = "";
2387 if (!ast_strlen_zero(actionid)) {
2388 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2391 if (!ao2_container_count(conference_bridges)) {
2392 astman_send_error(s, m, "No active conferences.");
2396 astman_send_listack(s, m, "Confbridge conferences will follow", "start");
2398 /* Traverse the conference list */
2399 i = ao2_iterator_init(conference_bridges, 0);
2400 while ((bridge = ao2_iterator_next(&i))) {
2405 "Event: ConfbridgeListRooms\r\n"
2407 "Conference: %s\r\n"
2415 bridge->markedusers,
2416 bridge->locked ? "Yes" : "No");
2419 ao2_ref(bridge, -1);
2421 ao2_iterator_destroy(&i);
2423 /* Send final confirmation */
2425 "Event: ConfbridgeListRoomsComplete\r\n"
2426 "EventList: Complete\r\n"
2429 "\r\n", totalitems, id_text);
2433 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
2435 const char *conference = astman_get_header(m, "Conference");
2436 const char *channel = astman_get_header(m, "Channel");
2439 if (ast_strlen_zero(conference)) {
2440 astman_send_error(s, m, "No Conference name provided.");
2443 if (ast_strlen_zero(channel)) {
2444 astman_send_error(s, m, "No channel name provided.");
2447 if (!ao2_container_count(conference_bridges)) {
2448 astman_send_error(s, m, "No active conferences.");
2452 res = generic_mute_unmute_helper(mute, conference, channel);
2455 astman_send_error(s, m, "No Conference by that name found.");
2457 } else if (res == -2) {
2458 astman_send_error(s, m, "No Channel by that name found in Conference.");
2462 astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2466 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
2468 return action_mute_unmute_helper(s, m, 0);
2470 static int action_confbridgemute(struct mansession *s, const struct message *m)
2472 return action_mute_unmute_helper(s, m, 1);
2475 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
2477 const char *conference = astman_get_header(m, "Conference");
2480 if (ast_strlen_zero(conference)) {
2481 astman_send_error(s, m, "No Conference name provided.");
2484 if (!ao2_container_count(conference_bridges)) {
2485 astman_send_error(s, m, "No active conferences.");
2488 if ((res = generic_lock_unlock_helper(lock, conference))) {
2489 astman_send_error(s, m, "No Conference by that name found.");
2492 astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
2495 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
2497 return action_lock_unlock_helper(s, m, 0);
2499 static int action_confbridgelock(struct mansession *s, const struct message *m)
2501 return action_lock_unlock_helper(s, m, 1);
2504 static int action_confbridgekick(struct mansession *s, const struct message *m)
2506 const char *conference = astman_get_header(m, "Conference");
2507 const char *channel = astman_get_header(m, "Channel");
2508 struct conference_bridge_user *participant = NULL;
2509 struct conference_bridge *bridge = NULL;
2510 struct conference_bridge tmp;
2513 if (ast_strlen_zero(conference)) {
2514 astman_send_error(s, m, "No Conference name provided.");
2517 if (!ao2_container_count(conference_bridges)) {
2518 astman_send_error(s, m, "No active conferences.");
2521 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2522 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2524 astman_send_error(s, m, "No Conference by that name found.");
2529 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2530 if (!strcasecmp(ast_channel_name(participant->chan), channel)) {
2531 participant->kicked = 1;
2532 ast_bridge_remove(bridge->bridge, participant->chan);
2538 ao2_ref(bridge, -1);
2541 astman_send_ack(s, m, "User kicked");
2543 astman_send_error(s, m, "No Channel by that name found in Conference.");
2548 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
2550 const char *conference = astman_get_header(m, "Conference");
2551 const char *recordfile = astman_get_header(m, "RecordFile");
2552 struct conference_bridge *bridge = NULL;
2553 struct conference_bridge tmp;
2555 if (ast_strlen_zero(conference)) {
2556 astman_send_error(s, m, "No Conference name provided.");
2559 if (!ao2_container_count(conference_bridges)) {
2560 astman_send_error(s, m, "No active conferences.");
2564 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2565 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2567 astman_send_error(s, m, "No Conference by that name found.");
2571 if (conf_is_recording(bridge)) {
2572 astman_send_error(s, m, "Conference is already being recorded.");
2573 ao2_ref(bridge, -1);
2577 if (!ast_strlen_zero(recordfile)) {
2579 ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file));
2583 if (conf_start_record(bridge)) {
2584 astman_send_error(s, m, "Internal error starting conference recording.");
2585 ao2_ref(bridge, -1);
2589 ao2_ref(bridge, -1);
2590 astman_send_ack(s, m, "Conference Recording Started.");
2593 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
2595 const char *conference = astman_get_header(m, "Conference");
2596 struct conference_bridge *bridge = NULL;
2597 struct conference_bridge tmp;
2599 if (ast_strlen_zero(conference)) {
2600 astman_send_error(s, m, "No Conference name provided.");
2603 if (!ao2_container_count(conference_bridges)) {
2604 astman_send_error(s, m, "No active conferences.");
2608 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2609 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2611 astman_send_error(s, m, "No Conference by that name found.");
2615 if (conf_stop_record(bridge)) {
2616 astman_send_error(s, m, "Internal error while stopping recording.");
2617 ao2_ref(bridge, -1);
2621 ao2_ref(bridge, -1);
2622 astman_send_ack(s, m, "Conference Recording Stopped.");
2626 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
2628 const char *conference = astman_get_header(m, "Conference");
2629 const char *channel = astman_get_header(m, "Channel");
2630 struct conference_bridge_user *participant = NULL;
2631 struct conference_bridge *bridge = NULL;
2632 struct conference_bridge tmp;
2634 if (ast_strlen_zero(conference)) {
2635 astman_send_error(s, m, "No Conference name provided.");
2638 if (ast_strlen_zero(channel)) {
2639 astman_send_error(s, m, "No channel name provided.");
2642 if (!ao2_container_count(conference_bridges)) {
2643 astman_send_error(s, m, "No active conferences.");
2647 ast_copy_string(tmp.name, conference, sizeof(tmp.name));
2648 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2650 astman_send_error(s, m, "No Conference by that name found.");
2654 /* find channel and set as video src. */
2656 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2657 if (!strncmp(channel, ast_channel_name(participant->chan), strlen(channel))) {
2658 ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
2663 ao2_ref(bridge, -1);
2665 /* do not access participant after bridge unlock. We are just
2666 * using this check to see if it was found or not */
2668 astman_send_error(s, m, "No channel by that name found in conference.");
2671 astman_send_ack(s, m, "Conference single video source set.");
2675 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
2678 struct conference_bridge *bridge = NULL;
2679 struct conference_bridge_user *participant = NULL;
2680 struct conference_bridge tmp;
2682 AST_DECLARE_APP_ARGS(args,
2684 AST_APP_ARG(confno);
2687 /* parse all the required arguments and make sure they exist. */
2688 if (ast_strlen_zero(data)) {
2691 parse = ast_strdupa(data);
2692 AST_STANDARD_APP_ARGS(args, parse);
2693 if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
2696 if (!ao2_container_count(conference_bridges)) {
2697 snprintf(buf, len, "0");
2700 ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
2701 bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
2703 snprintf(buf, len, "0");
2707 /* get the correct count for the type requested */
2709 if (!strncasecmp(args.type, "parties", 7)) {
2710 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2713 } else if (!strncasecmp(args.type, "admins", 6)) {
2714 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2715 if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
2719 } else if (!strncasecmp(args.type, "marked", 6)) {
2720 AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
2721 if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
2725 } else if (!strncasecmp(args.type, "locked", 6)) {
2726 count = bridge->locked;
2728 ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO. Should be one of: "
2729 "parties, admins, marked, or locked.\n", args.type);
2731 snprintf(buf, len, "%d", count);
2733 ao2_ref(bridge, -1);
2737 /*! \brief Called when module is being unloaded */
2738 static int unload_module(void)
2740 int res = ast_unregister_application(app);
2742 ast_custom_function_unregister(&confbridge_function);
2743 ast_custom_function_unregister(&confbridge_info_function);
2745 ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
2747 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
2748 ao2_ref(conference_bridges, -1);
2750 conf_destroy_config();
2752 ast_channel_unregister(&record_tech);
2753 record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
2755 res |= ast_manager_unregister("ConfbridgeList");
2756 res |= ast_manager_unregister("ConfbridgeListRooms");
2757 res |= ast_manager_unregister("ConfbridgeMute");
2758 res |= ast_manager_unregister("ConfbridgeUnmute");
2759 res |= ast_manager_unregister("ConfbridgeKick");
2760 res |= ast_manager_unregister("ConfbridgeUnlock");
2761 res |= ast_manager_unregister("ConfbridgeLock");
2762 res |= ast_manager_unregister("ConfbridgeStartRecord");
2763 res |= ast_manager_unregister("ConfbridgeStopRecord");
2768 /*! \brief Called when module is being loaded */
2769 static int load_module(void)
2772 if ((ast_custom_function_register(&confbridge_function))) {
2773 return AST_MODULE_LOAD_FAILURE;
2775 if ((ast_custom_function_register(&confbridge_info_function))) {
2776 return AST_MODULE_LOAD_FAILURE;
2778 if (!(record_tech.capabilities = ast_format_cap_alloc())) {
2779 return AST_MODULE_LOAD_FAILURE;
2781 ast_format_cap_add_all(record_tech.capabilities);
2782 if (ast_channel_register(&record_tech)) {
2783 ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
2784 return AST_MODULE_LOAD_FAILURE;
2786 /* Create a container to hold the conference bridges */
2787 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
2788 return AST_MODULE_LOAD_FAILURE;
2790 if (ast_register_application_xml(app, confbridge_exec)) {
2791 ao2_ref(conference_bridges, -1);
2792 return AST_MODULE_LOAD_FAILURE;
2795 res |= ast_cli_register_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
2796 res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
2797 res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
2798 res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
2799 res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
2800 res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
2801 res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
2802 res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
2803 res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord);
2804 res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord);
2805 res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
2807 conf_load_config(0);
2811 static int reload(void)
2813 return conf_load_config(1);
2816 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
2817 .load = load_module,
2818 .unload = unload_module,
2820 .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,