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
45 api_command_t conference_api_sub_commands[] = {
46 {"canvas-auto-clear", (void_fn_t) & conference_api_sub_canvas_auto_clear, CONF_API_SUB_ARGS_SPLIT, "canvas-auto-clear", "<canvas_id> <true|false>"},
47 {"count", (void_fn_t) & conference_api_sub_count, CONF_API_SUB_ARGS_SPLIT, "count", ""},
48 {"list", (void_fn_t) & conference_api_sub_list, CONF_API_SUB_ARGS_SPLIT, "list", "[delim <string>]|[count]"},
49 {"xml_list", (void_fn_t) & conference_api_sub_xml_list, CONF_API_SUB_ARGS_SPLIT, "xml_list", ""},
50 {"json_list", (void_fn_t) & conference_api_sub_json_list, CONF_API_SUB_ARGS_SPLIT, "json_list", "[compact]"},
51 {"energy", (void_fn_t) & conference_api_sub_energy, CONF_API_SUB_MEMBER_TARGET, "energy", "<member_id|all|last|non_moderator> [<newval>]"},
52 {"auto-energy", (void_fn_t) & conference_api_sub_auto_energy, CONF_API_SUB_MEMBER_TARGET, "auto-energy", "<member_id|all|last|non_moderator> [<newval>]"},
53 {"max-energy", (void_fn_t) & conference_api_sub_max_energy, CONF_API_SUB_MEMBER_TARGET, "max-energy", "<member_id|all|last|non_moderator> [<newval>]"},
54 {"agc", (void_fn_t) & conference_api_sub_agc, CONF_API_SUB_MEMBER_TARGET, "agc", "<member_id|all|last|non_moderator> [<newval>]"},
55 {"vid-canvas", (void_fn_t) & conference_api_sub_canvas, CONF_API_SUB_MEMBER_TARGET, "vid-canvas", "<member_id|all|last|non_moderator> [<newval>]"},
56 {"vid-watching-canvas", (void_fn_t) & conference_api_sub_watching_canvas, CONF_API_SUB_MEMBER_TARGET, "vid-watching-canvas", "<member_id|all|last|non_moderator> [<newval>]"},
57 {"vid-layer", (void_fn_t) & conference_api_sub_layer, CONF_API_SUB_MEMBER_TARGET, "vid-layer", "<member_id|all|last|non_moderator> [<newval>]"},
58 {"volume_in", (void_fn_t) & conference_api_sub_volume_in, CONF_API_SUB_MEMBER_TARGET, "volume_in", "<member_id|all|last|non_moderator> [<newval>]"},
59 {"volume_out", (void_fn_t) & conference_api_sub_volume_out, CONF_API_SUB_MEMBER_TARGET, "volume_out", "<member_id|all|last|non_moderator> [<newval>]"},
60 {"position", (void_fn_t) & conference_api_sub_position, CONF_API_SUB_MEMBER_TARGET, "position", "<member_id> <x>:<y>:<z>"},
61 {"auto-3d-position", (void_fn_t) & conference_api_sub_auto_position, CONF_API_SUB_ARGS_SPLIT, "auto-3d-position", "[on|off]"},
62 {"play", (void_fn_t) & conference_api_sub_play, CONF_API_SUB_ARGS_SPLIT, "play", "<file_path> [async|<member_id> [nomux]]"},
63 {"moh", (void_fn_t) & conference_api_sub_moh, CONF_API_SUB_ARGS_SPLIT, "moh", "<file_path>|toggle|[on|off]"},
64 {"pause_play", (void_fn_t) & conference_api_sub_pause_play, CONF_API_SUB_ARGS_SPLIT, "pause", "[<member_id>]"},
65 {"play_status", (void_fn_t) & conference_api_sub_play_status, CONF_API_SUB_ARGS_SPLIT, "play_status", "[<member_id>]"},
66 {"file_seek", (void_fn_t) & conference_api_sub_file_seek, CONF_API_SUB_ARGS_SPLIT, "file_seek", "[+-]<val> [<member_id>]"},
67 {"say", (void_fn_t) & conference_api_sub_say, CONF_API_SUB_ARGS_AS_ONE, "say", "<text>"},
68 {"saymember", (void_fn_t) & conference_api_sub_saymember, CONF_API_SUB_ARGS_AS_ONE, "saymember", "<member_id> <text>"},
69 {"cam", (void_fn_t) & conference_api_sub_cam, CONF_API_SUB_ARGS_SPLIT, "cam", ""},
70 {"stop", (void_fn_t) & conference_api_sub_stop, CONF_API_SUB_ARGS_SPLIT, "stop", "<[current|all|async|last]> [<member_id>]"},
71 {"dtmf", (void_fn_t) & conference_api_sub_dtmf, CONF_API_SUB_MEMBER_TARGET, "dtmf", "<[member_id|all|last|non_moderator]> <digits>"},
72 {"kick", (void_fn_t) & conference_api_sub_kick, CONF_API_SUB_MEMBER_TARGET, "kick", "<[member_id|all|last|non_moderator]> [<optional sound file>]"},
73 {"vid-flip", (void_fn_t) & conference_api_sub_vid_flip, CONF_API_SUB_MEMBER_TARGET, "vid-flip", "<[member_id|all|last|non_moderator]>"},
74 {"vid-border", (void_fn_t) & conference_api_sub_vid_border, CONF_API_SUB_MEMBER_TARGET, "vid-border", "<[member_id|all|last|non_moderator]>"},
75 {"hup", (void_fn_t) & conference_api_sub_hup, CONF_API_SUB_MEMBER_TARGET, "hup", "<[member_id|all|last|non_moderator]>"},
76 {"hold", (void_fn_t) & conference_api_sub_hold, CONF_API_SUB_MEMBER_TARGET, "hold", "<[member_id|all]|last|non_moderator> [file]"},
77 {"unhold", (void_fn_t) & conference_api_sub_unhold, CONF_API_SUB_MEMBER_TARGET, "unhold", "<[member_id|all]|last|non_moderator>"},
78 {"mute", (void_fn_t) & conference_api_sub_mute, CONF_API_SUB_MEMBER_TARGET, "mute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
79 {"tmute", (void_fn_t) & conference_api_sub_tmute, CONF_API_SUB_MEMBER_TARGET, "tmute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
80 {"unmute", (void_fn_t) & conference_api_sub_unmute, CONF_API_SUB_MEMBER_TARGET, "unmute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
81 {"vmute", (void_fn_t) & conference_api_sub_vmute, CONF_API_SUB_MEMBER_TARGET, "vmute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
82 {"tvmute", (void_fn_t) & conference_api_sub_tvmute, CONF_API_SUB_MEMBER_TARGET, "tvmute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
83 {"vmute-snap", (void_fn_t) & conference_api_sub_conference_video_vmute_snap, CONF_API_SUB_MEMBER_TARGET, "vmute-snap", "<[member_id|all]|last|non_moderator>"},
84 {"unvmute", (void_fn_t) & conference_api_sub_unvmute, CONF_API_SUB_MEMBER_TARGET, "unvmute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
85 {"vblind", (void_fn_t) & conference_api_sub_vblind, CONF_API_SUB_MEMBER_TARGET, "vblind", "<[member_id|all]|last|non_moderator> [<quiet>]"},
86 {"tvblind", (void_fn_t) & conference_api_sub_tvblind, CONF_API_SUB_MEMBER_TARGET, "tvblind", "<[member_id|all]|last|non_moderator> [<quiet>]"},
87 {"unvblind", (void_fn_t) & conference_api_sub_unvblind, CONF_API_SUB_MEMBER_TARGET, "unvblind", "<[member_id|all]|last|non_moderator> [<quiet>]"},
88 {"deaf", (void_fn_t) & conference_api_sub_deaf, CONF_API_SUB_MEMBER_TARGET, "deaf", "<[member_id|all]|last|non_moderator>"},
89 {"undeaf", (void_fn_t) & conference_api_sub_undeaf, CONF_API_SUB_MEMBER_TARGET, "undeaf", "<[member_id|all]|last|non_moderator>"},
90 {"vid-filter", (void_fn_t) & conference_api_sub_video_filter, CONF_API_SUB_MEMBER_TARGET, "vid-filter", "<[member_id|all]|last|non_moderator> <string>"},
91 {"relate", (void_fn_t) & conference_api_sub_relate, CONF_API_SUB_ARGS_SPLIT, "relate", "<member_id>[,<member_id>] <other_member_id>[,<other_member_id>] [nospeak|nohear|clear]"},
92 {"lock", (void_fn_t) & conference_api_sub_lock, CONF_API_SUB_ARGS_SPLIT, "lock", ""},
93 {"unlock", (void_fn_t) & conference_api_sub_unlock, CONF_API_SUB_ARGS_SPLIT, "unlock", ""},
94 {"dial", (void_fn_t) & conference_api_sub_dial, CONF_API_SUB_ARGS_SPLIT, "dial", "<endpoint_module_name>/<destination> <callerid number> <callerid name>"},
95 {"bgdial", (void_fn_t) & conference_api_sub_bgdial, CONF_API_SUB_ARGS_SPLIT, "bgdial", "<endpoint_module_name>/<destination> <callerid number> <callerid name>"},
96 {"transfer", (void_fn_t) & conference_api_sub_transfer, CONF_API_SUB_ARGS_SPLIT, "transfer", "<conference_name> <member id> [...<member id>]"},
97 {"record", (void_fn_t) & conference_api_sub_record, CONF_API_SUB_ARGS_SPLIT, "record", "<filename>"},
98 {"chkrecord", (void_fn_t) & conference_api_sub_check_record, CONF_API_SUB_ARGS_SPLIT, "chkrecord", "<confname>"},
99 {"norecord", (void_fn_t) & conference_api_sub_norecord, CONF_API_SUB_ARGS_SPLIT, "norecord", "<[filename|all]>"},
100 {"pause", (void_fn_t) & conference_api_sub_pauserec, CONF_API_SUB_ARGS_SPLIT, "pause", "<filename>"},
101 {"resume", (void_fn_t) & conference_api_sub_pauserec, CONF_API_SUB_ARGS_SPLIT, "resume", "<filename>"},
102 {"recording", (void_fn_t) & conference_api_sub_recording, CONF_API_SUB_ARGS_SPLIT, "recording", "[start|stop|check|pause|resume] [<filename>|all]"},
103 {"exit_sound", (void_fn_t) & conference_api_sub_exit_sound, CONF_API_SUB_ARGS_SPLIT, "exit_sound", "on|off|none|file <filename>"},
104 {"enter_sound", (void_fn_t) & conference_api_sub_enter_sound, CONF_API_SUB_ARGS_SPLIT, "enter_sound", "on|off|none|file <filename>"},
105 {"pin", (void_fn_t) & conference_api_sub_pin, CONF_API_SUB_ARGS_SPLIT, "pin", "<pin#>"},
106 {"nopin", (void_fn_t) & conference_api_sub_pin, CONF_API_SUB_ARGS_SPLIT, "nopin", ""},
107 {"get", (void_fn_t) & conference_api_sub_get, CONF_API_SUB_ARGS_SPLIT, "get", "<parameter-name>"},
108 {"set", (void_fn_t) & conference_api_sub_set, CONF_API_SUB_ARGS_SPLIT, "set", "<max_members|sound_prefix|caller_id_name|caller_id_number|endconference_grace_time> <value>"},
109 {"file-vol", (void_fn_t) & conference_api_sub_file_vol, CONF_API_SUB_ARGS_SPLIT, "file-vol", "<vol#>"},
110 {"floor", (void_fn_t) & conference_api_sub_floor, CONF_API_SUB_MEMBER_TARGET, "floor", "<member_id|last>"},
111 {"vid-floor", (void_fn_t) & conference_api_sub_vid_floor, CONF_API_SUB_MEMBER_TARGET, "vid-floor", "<member_id|last> [force]"},
112 {"vid-banner", (void_fn_t) & conference_api_sub_vid_banner, CONF_API_SUB_MEMBER_TARGET, "vid-banner", "<member_id|last> <text>"},
113 {"vid-mute-img", (void_fn_t) & conference_api_sub_vid_mute_img, CONF_API_SUB_MEMBER_TARGET, "vid-mute-img", "<member_id|last> [<path>|clear]"},
114 {"vid-logo-img", (void_fn_t) & conference_api_sub_vid_logo_img, CONF_API_SUB_MEMBER_TARGET, "vid-logo-img", "<member_id|last> [<path>|clear]"},
115 {"vid-codec-group", (void_fn_t) & conference_api_sub_vid_codec_group, CONF_API_SUB_MEMBER_TARGET, "vid-codec-group", "<member_id|last> [<group>|clear]"},
116 {"vid-res-id", (void_fn_t) & conference_api_sub_vid_res_id, CONF_API_SUB_ARGS_SPLIT, "vid-res-id", "<member_id>|all <val>|clear [force]"},
117 {"vid-role-id", (void_fn_t) & conference_api_sub_vid_role_id, CONF_API_SUB_MEMBER_TARGET, "vid-role-id", "<member_id|last> <val>|clear"},
118 {"get-uuid", (void_fn_t) & conference_api_sub_get_uuid, CONF_API_SUB_MEMBER_TARGET, "get-uuid", "<member_id|last>"},
119 {"clear-vid-floor", (void_fn_t) & conference_api_sub_clear_vid_floor, CONF_API_SUB_ARGS_AS_ONE, "clear-vid-floor", ""},
120 {"vid-layout", (void_fn_t) & conference_api_sub_vid_layout, CONF_API_SUB_ARGS_SPLIT, "vid-layout", "<layout name>|group <group name> [<canvas id>]"},
121 {"vid-write-png", (void_fn_t) & conference_api_sub_write_png, CONF_API_SUB_ARGS_SPLIT, "vid-write-png", "<path>"},
122 {"vid-fps", (void_fn_t) & conference_api_sub_vid_fps, CONF_API_SUB_ARGS_SPLIT, "vid-fps", "<fps>"},
123 {"vid-res", (void_fn_t) & conference_api_sub_vid_res, CONF_API_SUB_ARGS_SPLIT, "vid-res", "<WxH>"},
124 {"vid-fgimg", (void_fn_t) & conference_api_sub_canvas_fgimg, CONF_API_SUB_ARGS_SPLIT, "vid-fgimg", "<file> | clear [<canvas-id>]"},
125 {"vid-bgimg", (void_fn_t) & conference_api_sub_canvas_bgimg, CONF_API_SUB_ARGS_SPLIT, "vid-bgimg", "<file> | clear [<canvas-id>]"},
126 {"vid-bandwidth", (void_fn_t) & conference_api_sub_vid_bandwidth, CONF_API_SUB_ARGS_SPLIT, "vid-bandwidth", "<BW>"},
127 {"vid-personal", (void_fn_t) & conference_api_sub_vid_personal, CONF_API_SUB_ARGS_SPLIT, "vid-personal", "[on|off]"}
128 };
129
conference_api_sub_pause_play(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)130 switch_status_t conference_api_sub_pause_play(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
131 {
132 if (argc == 2) {
133 switch_mutex_lock(conference->mutex);
134 conference_fnode_toggle_pause(conference->fnode, stream);
135 switch_mutex_unlock(conference->mutex);
136
137 return SWITCH_STATUS_SUCCESS;
138 }
139
140 if (argc == 3) {
141 uint32_t id = atoi(argv[2]);
142 conference_member_t *member;
143
144 if ((member = conference_member_get(conference, id))) {
145 switch_mutex_lock(member->fnode_mutex);
146 conference_fnode_toggle_pause(member->fnode, stream);
147 switch_mutex_unlock(member->fnode_mutex);
148 switch_thread_rwlock_unlock(member->rwlock);
149 return SWITCH_STATUS_SUCCESS;
150 } else {
151 stream->write_function(stream, "-ERR Member: %u not found.\n", id);
152 }
153 }
154
155 return SWITCH_STATUS_GENERR;
156 }
157
conference_api_sub_play_status(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)158 switch_status_t conference_api_sub_play_status(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
159 {
160 if (argc == 2) {
161 switch_mutex_lock(conference->mutex);
162 conference_fnode_check_status(conference->fnode, stream);
163 switch_mutex_unlock(conference->mutex);
164
165 return SWITCH_STATUS_SUCCESS;
166 }
167
168 if (argc == 3) {
169 uint32_t id = atoi(argv[2]);
170 conference_member_t *member;
171
172 if ((member = conference_member_get(conference, id))) {
173 switch_mutex_lock(member->fnode_mutex);
174 conference_fnode_check_status(member->fnode, stream);
175 switch_mutex_unlock(member->fnode_mutex);
176 switch_thread_rwlock_unlock(member->rwlock);
177 return SWITCH_STATUS_SUCCESS;
178 } else {
179 stream->write_function(stream, "-ERR Member: %u not found.\n", id);
180 }
181 }
182
183 return SWITCH_STATUS_GENERR;
184 }
185
186 /* _In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream */
conference_api_main_real(const char * cmd,switch_core_session_t * session,switch_stream_handle_t * stream)187 switch_status_t conference_api_main_real(const char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream)
188 {
189 char *lbuf = NULL;
190 switch_status_t status = SWITCH_STATUS_SUCCESS;
191 char *http = NULL, *type = NULL;
192 int argc;
193 char *argv[25] = { 0 };
194
195 if (!cmd) {
196 cmd = "help";
197 }
198
199 if (stream->param_event) {
200 http = switch_event_get_header(stream->param_event, "http-host");
201 type = switch_event_get_header(stream->param_event, "content-type");
202 }
203
204 if (http) {
205 /* Output must be to a web browser */
206 if (type && !strcasecmp(type, "text/html")) {
207 stream->write_function(stream, "<pre>\n");
208 }
209 }
210
211 if (!(lbuf = strdup(cmd))) {
212 return status;
213 }
214
215 argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
216
217 /* try to find a command to execute */
218 if (argc && argv[0]) {
219 conference_obj_t *conference = NULL;
220
221 if ((conference = conference_find(argv[0], NULL))) {
222 if (argc >= 2) {
223 conference_api_dispatch(conference, stream, argc, argv, cmd, 1);
224 } else {
225 stream->write_function(stream, "Conference command, not specified.\nTry 'help'\n");
226 }
227 switch_thread_rwlock_unlock(conference->rwlock);
228
229 } else if (argv[0]) {
230 /* special case the list command, because it doesn't require a conference argument */
231 if (strcasecmp(argv[0], "list") == 0) {
232 conference_api_sub_list(NULL, stream, argc, argv);
233 } else if (strcasecmp(argv[0], "count") == 0) {
234 conference_api_sub_count(NULL, stream, argc, argv);
235 } else if (strcasecmp(argv[0], "xml_list") == 0) {
236 conference_api_sub_xml_list(NULL, stream, argc, argv);
237 } else if (strcasecmp(argv[0], "json_list") == 0) {
238 conference_api_sub_json_list(NULL, stream, argc, argv);
239 } else if (strcasecmp(argv[0], "help") == 0 || strcasecmp(argv[0], "commands") == 0) {
240 stream->write_function(stream, "%s\n", api_syntax);
241 } else if (argv[1] && strcasecmp(argv[1], "dial") == 0) {
242 if (conference_api_sub_dial(NULL, stream, argc, argv) != SWITCH_STATUS_SUCCESS) {
243 /* command returned error, so show syntax usage */
244 stream->write_function(stream, "%s %s", conference_api_sub_commands[CONF_API_COMMAND_DIAL].pcommand,
245 conference_api_sub_commands[CONF_API_COMMAND_DIAL].psyntax);
246 }
247 } else if (argv[1] && strcasecmp(argv[1], "bgdial") == 0) {
248 if (conference_api_sub_bgdial(NULL, stream, argc, argv) != SWITCH_STATUS_SUCCESS) {
249 /* command returned error, so show syntax usage */
250 stream->write_function(stream, "%s %s", conference_api_sub_commands[CONF_API_COMMAND_BGDIAL].pcommand,
251 conference_api_sub_commands[CONF_API_COMMAND_BGDIAL].psyntax);
252 }
253 } else {
254 stream->write_function(stream, "-ERR Conference %s not found\n", argv[0]);
255 }
256 }
257
258 } else {
259 int i;
260
261 for (i = 0; i < CONFFUNCAPISIZE; i++) {
262 stream->write_function(stream, "<conf name> %s %s\n", conference_api_sub_commands[i].pcommand, conference_api_sub_commands[i].psyntax);
263 }
264 }
265
266
267 switch_safe_free(lbuf);
268
269 return status;
270 }
271
conference_api_sub_syntax(char ** syntax)272 switch_status_t conference_api_sub_syntax(char **syntax)
273 {
274 /* build api interface help ".syntax" field string */
275 uint32_t i;
276 size_t nl = 0, ol = 0;
277 char cmd_str[256];
278 char *tmp = NULL, *p = strdup("");
279
280 for (i = 0; i < CONFFUNCAPISIZE; i++) {
281 nl = strlen(conference_api_sub_commands[i].pcommand) + strlen(conference_api_sub_commands[i].psyntax) + 5;
282
283 switch_snprintf(cmd_str, sizeof(cmd_str), "add conference ::conference::conference_list_conferences %s", conference_api_sub_commands[i].pcommand);
284 switch_console_set_complete(cmd_str);
285
286 if (p != NULL) {
287 ol = strlen(p);
288 }
289 tmp = realloc(p, ol + nl);
290 if (tmp != NULL) {
291 p = tmp;
292 strcat(p, "\t\t");
293 strcat(p, conference_api_sub_commands[i].pcommand);
294 if (!zstr(conference_api_sub_commands[i].psyntax)) {
295 strcat(p, " ");
296 strcat(p, conference_api_sub_commands[i].psyntax);
297 }
298 if (i < CONFFUNCAPISIZE - 1) {
299 strcat(p, "\n");
300 }
301 } else {
302 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't realloc\n");
303 switch_safe_free(p);
304 return SWITCH_STATUS_TERM;
305 }
306 }
307
308 *syntax = p;
309
310 return SWITCH_STATUS_SUCCESS;
311 }
312
conference_api_sub_mute(conference_member_t * member,switch_stream_handle_t * stream,void * data)313 switch_status_t conference_api_sub_mute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
314 {
315 switch_event_t *event;
316
317 if (member == NULL)
318 return SWITCH_STATUS_GENERR;
319
320 conference_utils_member_clear_flag_locked(member, MFLAG_CAN_SPEAK);
321 conference_utils_member_clear_flag_locked(member, MFLAG_TALKING);
322
323 if (member->session && !conference_utils_member_test_flag(member, MFLAG_MUTE_DETECT) && !conference_utils_member_test_flag(member, MFLAG_HOLD)) {
324 switch_core_media_hard_mute(member->session, SWITCH_TRUE);
325 }
326
327 if (!(data) || !strstr((char *) data, "quiet")) {
328 conference_utils_member_set_flag(member, MFLAG_INDICATE_MUTE);
329 }
330 conference_member_set_score_iir(member, 0);
331
332 if (stream != NULL) {
333 stream->write_function(stream, "+OK mute %u\n", member->id);
334 }
335
336 if (test_eflag(member->conference, EFLAG_MUTE_MEMBER) &&
337 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
338 conference_member_add_event_data(member, event);
339 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "mute-member");
340 switch_event_fire(&event);
341 }
342
343 if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
344 conference_al_gen_arc(member->conference, NULL);
345 }
346
347 conference_member_update_status_field(member);
348
349 return SWITCH_STATUS_SUCCESS;
350 }
351
conference_api_sub_unhold(conference_member_t * member,switch_stream_handle_t * stream,void * data)352 switch_status_t conference_api_sub_unhold(conference_member_t *member, switch_stream_handle_t *stream, void *data)
353 {
354 mcu_layer_t *layer = NULL;
355 switch_event_t *event;
356
357 if (member == NULL)
358 return SWITCH_STATUS_GENERR;
359
360 conference_utils_member_clear_flag_locked(member, MFLAG_HOLD);
361
362 if (member->session && !conference_utils_member_test_flag(member, MFLAG_MUTE_DETECT)) {
363 switch_core_media_hard_mute(member->session, SWITCH_FALSE);
364 }
365
366 conference_member_stop_file(member, FILE_STOP_ALL);
367
368 if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) != SWITCH_MEDIA_FLOW_SENDONLY) {
369 if ((layer = conference_video_get_layer_locked(member))) {
370 layer->clear = 1;
371 conference_video_release_layer(&layer);
372 }
373
374 conference_video_reset_video_bitrate_counters(member);
375
376 if (member->channel) {
377 switch_channel_clear_flag(member->channel, CF_VIDEO_PAUSE_READ);
378 switch_channel_video_sync(member->channel);
379 }
380 }
381
382 if (stream != NULL) {
383 stream->write_function(stream, "+OK unhold %u\n", member->id);
384 }
385
386 if (test_eflag(member->conference, EFLAG_HOLD_MEMBER) &&
387 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
388 conference_member_add_event_data(member, event);
389 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "unhold-member");
390 switch_event_fire(&event);
391 }
392
393 if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
394 conference_al_gen_arc(member->conference, NULL);
395 }
396
397 conference_member_update_status_field(member);
398
399 return SWITCH_STATUS_SUCCESS;
400 }
401
conference_api_sub_hold(conference_member_t * member,switch_stream_handle_t * stream,void * data)402 switch_status_t conference_api_sub_hold(conference_member_t *member, switch_stream_handle_t *stream, void *data)
403 {
404 switch_event_t *event;
405
406 if (member == NULL)
407 return SWITCH_STATUS_GENERR;
408
409 conference_utils_member_clear_flag_locked(member, MFLAG_TALKING);
410
411 if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) != SWITCH_MEDIA_FLOW_SENDONLY) {
412 conference_video_reset_video_bitrate_counters(member);
413
414 if (member->channel) {
415 switch_channel_set_flag(member->channel, CF_VIDEO_PAUSE_READ);
416 switch_core_session_request_video_refresh(member->session);
417 switch_channel_video_sync(member->channel);
418 }
419 }
420
421 if (member->session) {
422 switch_core_media_hard_mute(member->session, SWITCH_TRUE);
423 }
424
425 conference_utils_member_set_flag(member, MFLAG_HOLD);
426
427 conference_member_set_score_iir(member, 0);
428
429 if (!zstr(data)) {
430 conference_member_play_file(member, data, 0, SWITCH_FALSE);
431 }
432
433 if (stream != NULL) {
434 stream->write_function(stream, "+OK hold %u\n", member->id);
435 }
436
437 if (test_eflag(member->conference, EFLAG_HOLD_MEMBER) &&
438 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
439 conference_member_add_event_data(member, event);
440 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "hold-member");
441 switch_event_fire(&event);
442 }
443
444 if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
445 conference_al_gen_arc(member->conference, NULL);
446 }
447
448 conference_member_update_status_field(member);
449
450 return SWITCH_STATUS_SUCCESS;
451 }
452
453
conference_api_sub_tmute(conference_member_t * member,switch_stream_handle_t * stream,void * data)454 switch_status_t conference_api_sub_tmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
455 {
456
457 if (member == NULL)
458 return SWITCH_STATUS_GENERR;
459
460 if (conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)) {
461 return conference_api_sub_mute(member, stream, data);
462 }
463
464 return conference_api_sub_unmute(member, stream, data);
465 }
466
467
conference_api_sub_unmute(conference_member_t * member,switch_stream_handle_t * stream,void * data)468 switch_status_t conference_api_sub_unmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
469 {
470 switch_event_t *event;
471
472 if (member == NULL)
473 return SWITCH_STATUS_GENERR;
474
475 conference_utils_member_set_flag_locked(member, MFLAG_CAN_SPEAK);
476
477 if (member->session && !conference_utils_member_test_flag(member, MFLAG_MUTE_DETECT)) {
478 switch_core_media_hard_mute(member->session, SWITCH_FALSE);
479 }
480
481 if (!(data) || !strstr((char *) data, "quiet")) {
482 conference_utils_member_set_flag(member, MFLAG_INDICATE_UNMUTE);
483 }
484
485 if (stream != NULL) {
486 stream->write_function(stream, "+OK unmute %u\n", member->id);
487 }
488
489 if (test_eflag(member->conference, EFLAG_MUTE_MEMBER) &&
490 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
491 conference_member_add_event_data(member, event);
492 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "unmute-member");
493 switch_event_fire(&event);
494 }
495
496 if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
497 conference_al_gen_arc(member->conference, NULL);
498 }
499
500 conference_member_update_status_field(member);
501
502 return SWITCH_STATUS_SUCCESS;
503 }
504
conference_api_sub_conference_video_vmute_snap(conference_member_t * member,switch_stream_handle_t * stream,void * data)505 switch_status_t conference_api_sub_conference_video_vmute_snap(conference_member_t *member, switch_stream_handle_t *stream, void *data)
506 {
507 switch_bool_t clear = SWITCH_FALSE;
508
509 if (member == NULL)
510 return SWITCH_STATUS_GENERR;
511
512 if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) {
513 return SWITCH_STATUS_SUCCESS;
514 }
515
516 if (!member->conference->canvases[0]) {
517 if (stream) stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
518 return SWITCH_STATUS_SUCCESS;
519 }
520
521 if (conference_utils_member_test_flag(member, MFLAG_HOLD)) {
522 if (stream) stream->write_function(stream, "-ERR member %u is on hold\n", member->id);
523 return SWITCH_STATUS_SUCCESS;
524 }
525
526 if (stream != NULL) {
527 stream->write_function(stream, "+OK vmute image snapped %u\n", member->id);
528 }
529
530 if (data && !strcasecmp((char *)data, "clear")) {
531 clear = SWITCH_TRUE;
532 }
533
534 conference_video_vmute_snap(member, clear);
535
536 return SWITCH_STATUS_SUCCESS;
537 }
538
conference_api_sub_canvas_auto_clear(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)539 switch_status_t conference_api_sub_canvas_auto_clear(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
540 {
541 int canvas_id_start = 0;
542 int canvas_id_end = 0;
543 int i = 0;
544
545 if (argc < 3) {
546 stream->write_function(stream, "+OK");
547
548 for (i = 0; i < conference->canvas_count; i++) {
549 stream->write_function(stream, " canvas %d auto_clear=%s", i + 1, conference->canvases[i]->disable_auto_clear ? "false" : "true");
550 }
551
552 stream->write_function(stream, "\n");
553 return SWITCH_STATUS_SUCCESS;
554 }
555
556 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s\n", argv[1]);
557
558 canvas_id_start = atoi(argv[2]);
559
560 if (canvas_id_start == 0) {
561 canvas_id_end = conference->canvas_count - 1;
562 } else {
563 canvas_id_start--;
564 canvas_id_end = canvas_id_start;
565 }
566
567 stream->write_function(stream, "+OK");
568 switch_mutex_lock(conference->canvas_mutex);
569
570 for (i = canvas_id_start; i<= canvas_id_end; i++) {
571 conference->canvases[i]->disable_auto_clear = !switch_true(argv[3]);
572 stream->write_function(stream, " canvas %d auto_clear=%s", i + 1, argv[3]);
573 }
574
575 switch_mutex_unlock(conference->canvas_mutex);
576 stream->write_function(stream, "\n");
577
578 return SWITCH_STATUS_SUCCESS;
579 }
580
conference_api_sub_vmute(conference_member_t * member,switch_stream_handle_t * stream,void * data)581 switch_status_t conference_api_sub_vmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
582 {
583 switch_event_t *event;
584
585 if (member == NULL)
586 return SWITCH_STATUS_GENERR;
587
588 if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) {
589 return SWITCH_STATUS_SUCCESS;
590 }
591
592 if (conference_utils_member_test_flag(member, MFLAG_HOLD)) {
593 if (stream) stream->write_function(stream, "-ERR member %u is on hold\n", member->id);
594 return SWITCH_STATUS_SUCCESS;
595 }
596
597 conference_utils_member_clear_flag_locked(member, MFLAG_CAN_BE_SEEN);
598 conference_video_reset_video_bitrate_counters(member);
599
600 if (member->channel) {
601 switch_channel_set_flag(member->channel, CF_VIDEO_PAUSE_READ);
602 switch_core_session_request_video_refresh(member->session);
603 switch_channel_video_sync(member->channel);
604 }
605
606 if (!(data) || !strstr((char *) data, "quiet")) {
607 conference_utils_member_set_flag(member, MFLAG_INDICATE_MUTE);
608 }
609
610 if (stream != NULL) {
611 stream->write_function(stream, "+OK vmute %u\n", member->id);
612 }
613
614 if (test_eflag(member->conference, EFLAG_MUTE_MEMBER) &&
615 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
616 conference_member_add_event_data(member, event);
617 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "vmute-member");
618 switch_event_fire(&event);
619 }
620
621 conference_member_update_status_field(member);
622
623 return SWITCH_STATUS_SUCCESS;
624 }
625
626
conference_api_sub_tvmute(conference_member_t * member,switch_stream_handle_t * stream,void * data)627 switch_status_t conference_api_sub_tvmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
628 {
629
630 if (member == NULL)
631 return SWITCH_STATUS_GENERR;
632
633 if (conference_utils_member_test_flag(member, MFLAG_HOLD)) {
634 if (stream) stream->write_function(stream, "-ERR member %u is on hold\n", member->id);
635 return SWITCH_STATUS_SUCCESS;
636 }
637
638 if (conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN)) {
639 return conference_api_sub_vmute(member, stream, data);
640 }
641
642 return conference_api_sub_unvmute(member, stream, data);
643 }
644
645
conference_api_sub_unvmute(conference_member_t * member,switch_stream_handle_t * stream,void * data)646 switch_status_t conference_api_sub_unvmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
647 {
648 switch_event_t *event;
649 mcu_layer_t *layer = NULL;
650
651 if (member == NULL)
652 return SWITCH_STATUS_GENERR;
653
654 if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) {
655 return SWITCH_STATUS_SUCCESS;
656 }
657
658 if (conference_utils_member_test_flag(member, MFLAG_HOLD)) {
659 if (stream) stream->write_function(stream, "-ERR member %u is on hold\n", member->id);
660 return SWITCH_STATUS_SUCCESS;
661 }
662
663 if ((layer = conference_video_get_layer_locked(member))) {
664 layer->clear = 1;
665 conference_video_release_layer(&layer);
666 }
667
668 conference_utils_member_set_flag_locked(member, MFLAG_CAN_BE_SEEN);
669 conference_video_reset_video_bitrate_counters(member);
670
671 if (member->channel) {
672 switch_channel_clear_flag(member->channel, CF_VIDEO_PAUSE_READ);
673 switch_channel_video_sync(member->channel);
674 }
675
676 if (!(data) || !strstr((char *) data, "quiet")) {
677 conference_utils_member_set_flag(member, MFLAG_INDICATE_UNMUTE);
678 }
679
680 if (stream != NULL) {
681 stream->write_function(stream, "+OK unvmute %u\n", member->id);
682 }
683
684 if (test_eflag(member->conference, EFLAG_MUTE_MEMBER) &&
685 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
686 conference_member_add_event_data(member, event);
687 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "unvmute-member");
688 switch_event_fire(&event);
689 }
690
691
692 conference_member_update_status_field(member);
693
694 return SWITCH_STATUS_SUCCESS;
695 }
696
conference_api_sub_vblind(conference_member_t * member,switch_stream_handle_t * stream,void * data)697 switch_status_t conference_api_sub_vblind(conference_member_t *member, switch_stream_handle_t *stream, void *data)
698 {
699 switch_event_t *event;
700
701 if (member == NULL)
702 return SWITCH_STATUS_GENERR;
703
704 conference_utils_member_clear_flag_locked(member, MFLAG_CAN_SEE);
705
706 if (!conference_utils_member_test_flag(member, MFLAG_HOLD)) {
707 switch_core_session_write_blank_video(member->session, 50);
708 conference_video_reset_video_bitrate_counters(member);
709 }
710
711 if (!(data) || !strstr((char *) data, "quiet")) {
712 conference_utils_member_set_flag(member, MFLAG_INDICATE_BLIND);
713 }
714
715 if (stream) stream->write_function(stream, "+OK vblind %u\n", member->id);
716
717 if (test_eflag(member->conference, EFLAG_BLIND_MEMBER) &&
718 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
719 conference_member_add_event_data(member, event);
720 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "vblind-member");
721 switch_event_fire(&event);
722 }
723
724 conference_member_update_status_field(member);
725
726 return SWITCH_STATUS_SUCCESS;
727 }
728
729
conference_api_sub_tvblind(conference_member_t * member,switch_stream_handle_t * stream,void * data)730 switch_status_t conference_api_sub_tvblind(conference_member_t *member, switch_stream_handle_t *stream, void *data)
731 {
732
733 if (member == NULL)
734 return SWITCH_STATUS_GENERR;
735
736 if (conference_utils_member_test_flag(member, MFLAG_CAN_SEE)) {
737 return conference_api_sub_vblind(member, stream, data);
738 }
739
740 return conference_api_sub_unvblind(member, stream, data);
741 }
742
743
conference_api_sub_unvblind(conference_member_t * member,switch_stream_handle_t * stream,void * data)744 switch_status_t conference_api_sub_unvblind(conference_member_t *member, switch_stream_handle_t *stream, void *data)
745 {
746 switch_event_t *event;
747
748 if (member == NULL)
749 return SWITCH_STATUS_GENERR;
750
751 conference_utils_member_set_flag_locked(member, MFLAG_CAN_SEE);
752
753 if (!conference_utils_member_test_flag(member, MFLAG_HOLD)) {
754 conference_video_reset_video_bitrate_counters(member);
755 switch_channel_set_flag(member->channel, CF_VIDEO_REFRESH_REQ);
756 }
757
758 if (!(data) || !strstr((char *) data, "quiet")) {
759 conference_utils_member_set_flag(member, MFLAG_INDICATE_UNBLIND);
760 }
761
762 if (stream != NULL) {
763 stream->write_function(stream, "+OK unvblind %u\n", member->id);
764 }
765
766 if (test_eflag(member->conference, EFLAG_BLIND_MEMBER) &&
767 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
768 conference_member_add_event_data(member, event);
769 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "unvblind-member");
770 switch_event_fire(&event);
771 }
772
773
774 conference_member_update_status_field(member);
775
776 return SWITCH_STATUS_SUCCESS;
777 }
778
conference_api_sub_deaf(conference_member_t * member,switch_stream_handle_t * stream,void * data)779 switch_status_t conference_api_sub_deaf(conference_member_t *member, switch_stream_handle_t *stream, void *data)
780 {
781 switch_event_t *event;
782
783 if (member == NULL)
784 return SWITCH_STATUS_GENERR;
785
786 conference_utils_member_clear_flag_locked(member, MFLAG_CAN_HEAR);
787
788 if (!(data) || !strstr((char *) data, "quiet")) {
789 conference_utils_member_set_flag(member, MFLAG_INDICATE_DEAF);
790 }
791
792 if (stream != NULL) {
793 stream->write_function(stream, "+OK deaf %u\n", member->id);
794 }
795 if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
796 conference_member_add_event_data(member, event);
797 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "deaf-member");
798 switch_event_fire(&event);
799 }
800
801 if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
802 conference_al_gen_arc(member->conference, NULL);
803 }
804
805 conference_member_update_status_field(member);
806
807 return SWITCH_STATUS_SUCCESS;
808 }
809
conference_api_sub_video_filter(conference_member_t * member,switch_stream_handle_t * stream,void * data)810 switch_status_t conference_api_sub_video_filter(conference_member_t *member, switch_stream_handle_t *stream, void *data)
811 {
812 char *filter_str = (char *) data;
813
814 switch_core_video_parse_filter_string(&member->video_filters, filter_str);
815
816 stream->write_function(stream, "+OK\n");
817
818 return SWITCH_STATUS_SUCCESS;
819 }
820
conference_api_sub_undeaf(conference_member_t * member,switch_stream_handle_t * stream,void * data)821 switch_status_t conference_api_sub_undeaf(conference_member_t *member, switch_stream_handle_t *stream, void *data)
822 {
823 switch_event_t *event;
824
825 if (member == NULL)
826 return SWITCH_STATUS_GENERR;
827
828 conference_utils_member_set_flag_locked(member, MFLAG_CAN_HEAR);
829
830 if (!(data) || !strstr((char *) data, "quiet")) {
831 conference_utils_member_set_flag(member, MFLAG_INDICATE_UNDEAF);
832 }
833
834 if (stream != NULL) {
835 stream->write_function(stream, "+OK undeaf %u\n", member->id);
836 }
837 if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
838 conference_member_add_event_data(member, event);
839 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "undeaf-member");
840 switch_event_fire(&event);
841 }
842
843 if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
844 conference_al_gen_arc(member->conference, NULL);
845 }
846
847 conference_member_update_status_field(member);
848
849 return SWITCH_STATUS_SUCCESS;
850 }
851
conference_api_sub_hup(conference_member_t * member,switch_stream_handle_t * stream,void * data)852 switch_status_t conference_api_sub_hup(conference_member_t *member, switch_stream_handle_t *stream, void *data)
853 {
854 switch_event_t *event;
855
856 if (member == NULL) {
857 if (stream != NULL) {
858 stream->write_function(stream, "-ERR Invalid member!\n");
859 }
860 return SWITCH_STATUS_GENERR;
861 }
862
863 conference_utils_member_clear_flag(member, MFLAG_RUNNING);
864 if (stream != NULL) {
865 stream->write_function(stream, "+OK hup %u\n", member->id);
866 }
867
868 if (member->conference && test_eflag(member->conference, EFLAG_HUP_MEMBER)) {
869 if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
870 conference_member_add_event_data(member, event);
871 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "hup-member");
872 switch_event_fire(&event);
873 }
874 }
875
876 return SWITCH_STATUS_SUCCESS;
877 }
878
conference_api_sub_kick(conference_member_t * member,switch_stream_handle_t * stream,void * data)879 switch_status_t conference_api_sub_kick(conference_member_t *member, switch_stream_handle_t *stream, void *data)
880 {
881 switch_event_t *event;
882
883 if (member == NULL) {
884 return SWITCH_STATUS_GENERR;
885 }
886
887 conference_utils_member_clear_flag(member, MFLAG_RUNNING);
888 conference_utils_member_set_flag_locked(member, MFLAG_KICKED);
889 switch_core_session_kill_channel(member->session, SWITCH_SIG_BREAK);
890
891 if (data && member->session) {
892 member->kicked_sound = switch_core_session_strdup(member->session, (char *) data);
893 }
894
895 if (stream != NULL) {
896 stream->write_function(stream, "+OK kicked %u\n", member->id);
897 }
898
899 if (member->conference && test_eflag(member->conference, EFLAG_KICK_MEMBER)) {
900 if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
901 conference_member_add_event_data(member, event);
902 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "kick-member");
903 switch_event_fire(&event);
904 }
905 }
906
907 return SWITCH_STATUS_SUCCESS;
908 }
909
conference_api_sub_vid_border(conference_member_t * member,switch_stream_handle_t * stream,void * data)910 switch_status_t conference_api_sub_vid_border(conference_member_t *member, switch_stream_handle_t *stream, void *data)
911 {
912 char *arg = (char *) data;
913 mcu_layer_t *layer = NULL;
914 int len = 5;
915
916 if (member == NULL) {
917 return SWITCH_STATUS_GENERR;
918 }
919
920 if (zstr(arg)) {
921 if (stream) {
922 stream->write_function(stream, "-ERR No text supplied\n", switch_channel_get_name(member->channel));
923 }
924 goto end;
925 }
926
927 layer = conference_video_get_layer_locked(member);
928
929 if (!layer) {
930 if (stream) {
931 stream->write_function(stream, "-ERR Channel %s is not in a video layer\n", switch_channel_get_name(member->channel));
932 }
933 goto end;
934 }
935
936 if (!strcasecmp(arg, "toggle")) {
937 if (member->video_manual_border) {
938 len = 0;
939 } else {
940 len = 5;
941 }
942 } else {
943 len = atoi(arg);
944 }
945
946 if (len < 0 || len > 20) {
947 len = 0;
948 }
949
950 member->video_manual_border = len;
951 layer->manual_border = len;
952
953 if (stream) {
954 stream->write_function(stream, "+OK\n");
955 }
956
957 end:
958
959 if (layer) {
960 conference_video_release_layer(&layer);
961 }
962
963 return SWITCH_STATUS_SUCCESS;
964 }
965
966
conference_api_sub_vid_flip(conference_member_t * member,switch_stream_handle_t * stream,void * data)967 switch_status_t conference_api_sub_vid_flip(conference_member_t *member, switch_stream_handle_t *stream, void *data)
968 {
969 char *arg = (char *) data;
970
971 if (member == NULL) {
972 return SWITCH_STATUS_GENERR;
973 }
974
975 if ((conference_utils_member_test_flag(member, MFLAG_FLIP_VIDEO) || conference_utils_member_test_flag(member, MFLAG_MIRROR_VIDEO)) && !arg) {
976 conference_utils_member_clear_flag_locked(member, MFLAG_FLIP_VIDEO);
977 conference_utils_member_clear_flag_locked(member, MFLAG_ROTATE_VIDEO);
978 conference_utils_member_clear_flag_locked(member, MFLAG_MIRROR_VIDEO);
979 } else {
980
981 if (arg && !strcasecmp(arg, "mirror")) {
982 if (conference_utils_member_test_flag(member, MFLAG_MIRROR_VIDEO)) {
983 conference_utils_member_clear_flag_locked(member, MFLAG_MIRROR_VIDEO);
984 } else {
985 conference_utils_member_set_flag_locked(member, MFLAG_MIRROR_VIDEO);
986 }
987 } else {
988 conference_utils_member_set_flag_locked(member, MFLAG_FLIP_VIDEO);
989
990 if (arg) {
991 if (!strcasecmp(arg, "rotate")) {
992 conference_utils_member_set_flag_locked(member, MFLAG_ROTATE_VIDEO);
993 } else if (switch_is_number(arg)) {
994 int num = atoi(arg);
995
996 if (num == 0 || num == 90 || num == 180 || num == 270) {
997 member->flip = num;
998 }
999 }
1000 } else {
1001 member->flip = 180;
1002 }
1003 }
1004 }
1005
1006 if (stream != NULL) {
1007 stream->write_function(stream, "+OK flipped %u\n", member->id);
1008 }
1009
1010
1011 return SWITCH_STATUS_SUCCESS;
1012 }
1013
1014
conference_api_sub_dtmf(conference_member_t * member,switch_stream_handle_t * stream,void * data)1015 switch_status_t conference_api_sub_dtmf(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1016 {
1017 switch_event_t *event;
1018 char *dtmf = (char *) data;
1019
1020 if (member == NULL) {
1021 if (stream != NULL) stream->write_function(stream, "-ERR Invalid member!\n");
1022 return SWITCH_STATUS_GENERR;
1023 }
1024
1025 if (zstr(dtmf)) {
1026 if (stream != NULL) stream->write_function(stream, "-ERR Invalid input!\n");
1027 return SWITCH_STATUS_GENERR;
1028 } else {
1029 char *p;
1030
1031 for(p = dtmf; p && *p; p++) {
1032 switch_dtmf_t *dt, digit = { *p, SWITCH_DEFAULT_DTMF_DURATION };
1033
1034 switch_zmalloc(dt, sizeof(*dt));
1035 *dt = digit;
1036
1037 switch_queue_push(member->dtmf_queue, dt);
1038 switch_core_session_kill_channel(member->session, SWITCH_SIG_BREAK);
1039 }
1040 }
1041
1042 if (stream != NULL) {
1043 stream->write_function(stream, "+OK sent %s to %u\n", (char *) data, member->id);
1044 }
1045
1046 if (test_eflag(member->conference, EFLAG_DTMF_MEMBER) &&
1047 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1048 conference_member_add_event_data(member, event);
1049 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "dtmf-member");
1050 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Digits", dtmf);
1051 switch_event_fire(&event);
1052 }
1053
1054 return SWITCH_STATUS_SUCCESS;
1055 }
1056
conference_api_sub_watching_canvas(conference_member_t * member,switch_stream_handle_t * stream,void * data)1057 switch_status_t conference_api_sub_watching_canvas(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1058 {
1059 int index;
1060 char *val = (char *) data;
1061
1062 if (member->conference->canvas_count == 1) {
1063 stream->write_function(stream, "-ERR Only 1 Canvas\n");
1064 return SWITCH_STATUS_SUCCESS;
1065 }
1066
1067 index = conference_member_get_canvas_id(member, val, SWITCH_TRUE);
1068
1069 if (index < 0) {
1070 stream->write_function(stream, "-ERR Invalid DATA\n");
1071 return SWITCH_STATUS_SUCCESS;
1072 }
1073
1074 member->watching_canvas_id = index;
1075 conference_video_reset_member_codec_index(member);
1076 switch_core_session_request_video_refresh(member->session);
1077 switch_core_media_gen_key_frame(member->session);
1078 member->conference->canvases[index]->send_keyframe = 10;
1079 member->conference->canvases[index]->refresh = 1;
1080 stream->write_function(stream, "+OK watching canvas %d\n", index + 1);
1081
1082 conference_member_update_status_field(member);
1083
1084 return SWITCH_STATUS_SUCCESS;
1085 }
1086
conference_api_sub_canvas(conference_member_t * member,switch_stream_handle_t * stream,void * data)1087 switch_status_t conference_api_sub_canvas(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1088 {
1089 int index;
1090 char *val = (char *) data;
1091 //mcu_canvas_t *canvas = NULL;
1092
1093 if (member->conference->canvas_count == 1) {
1094 stream->write_function(stream, "-ERR Only 1 Canvas\n");
1095 return SWITCH_STATUS_SUCCESS;
1096 }
1097
1098 switch_mutex_lock(member->conference->canvas_mutex);
1099
1100 index = conference_member_get_canvas_id(member, val, SWITCH_FALSE);
1101
1102 if (index < 0) {
1103 stream->write_function(stream, "-ERR Invalid DATA\n");
1104 switch_mutex_unlock(member->conference->canvas_mutex);
1105 return SWITCH_STATUS_SUCCESS;
1106 }
1107
1108 conference_video_detach_video_layer(member);
1109 member->canvas_id = index;
1110 member->layer_timeout = DEFAULT_LAYER_TIMEOUT;
1111
1112 //canvas = member->conference->canvases[member->canvas_id];
1113 //conference_video_attach_video_layer(member, canvas, index);
1114 conference_video_reset_member_codec_index(member);
1115 switch_mutex_unlock(member->conference->canvas_mutex);
1116
1117 switch_core_session_request_video_refresh(member->session);
1118 switch_core_media_gen_key_frame(member->session);
1119 member->conference->canvases[index]->send_keyframe = 10;
1120 member->conference->canvases[index]->refresh = 1;
1121 stream->write_function(stream, "+OK canvas %d\n", member->canvas_id + 1);
1122
1123 return SWITCH_STATUS_SUCCESS;
1124 }
1125
1126
1127
conference_api_sub_layer(conference_member_t * member,switch_stream_handle_t * stream,void * data)1128 switch_status_t conference_api_sub_layer(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1129 {
1130 int index = -1;
1131 mcu_canvas_t *canvas = NULL;
1132 char *val = (char *) data;
1133
1134 if (!val) {
1135 stream->write_function(stream, "-ERR Invalid DATA\n");
1136 return SWITCH_STATUS_SUCCESS;
1137 }
1138
1139 if (member->canvas_id < 0) {
1140 stream->write_function(stream, "-ERR Invalid Canvas\n");
1141 return SWITCH_STATUS_FALSE;
1142 }
1143
1144
1145 switch_mutex_lock(member->conference->canvas_mutex);
1146
1147 if (switch_is_number(val)) {
1148 index = atoi(val) - 1;
1149
1150 if (index < 0) {
1151 index = 0;
1152 }
1153 } else {
1154 index = member->video_layer_id;
1155
1156 if (index < 0) index = 0;
1157
1158 if (!strcasecmp(val, "next")) {
1159 index++;
1160 } else if (!strcasecmp(val, "prev")) {
1161 index--;
1162 }
1163 }
1164
1165 canvas = member->conference->canvases[member->canvas_id];
1166
1167 if (index >= canvas->total_layers) {
1168 index = 0;
1169 }
1170
1171 if (index < 0) {
1172 index = canvas->total_layers - 1;
1173 }
1174
1175 conference_video_attach_video_layer(member, canvas, index);
1176 switch_mutex_unlock(member->conference->canvas_mutex);
1177
1178 switch_core_session_request_video_refresh(member->session);
1179 switch_core_media_gen_key_frame(member->session);
1180 canvas->send_keyframe = 10;
1181 canvas->refresh = 1;
1182 stream->write_function(stream, "+OK layer %d\n", member->video_layer_id + 1);
1183
1184 return SWITCH_STATUS_SUCCESS;
1185 }
1186
1187
conference_api_sub_energy(conference_member_t * member,switch_stream_handle_t * stream,void * data)1188 switch_status_t conference_api_sub_energy(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1189 {
1190 switch_event_t *event;
1191
1192 if (member == NULL) {
1193 return SWITCH_STATUS_GENERR;
1194 }
1195
1196 if (data) {
1197 lock_member(member);
1198 if (!strcasecmp(data, "up")) {
1199 member->energy_level += 200;
1200 if (member->energy_level > 1800) {
1201 member->energy_level = 1800;
1202 }
1203 } else if (!strcasecmp(data, "down")) {
1204 member->energy_level -= 200;
1205 if (member->energy_level < 0) {
1206 member->energy_level = 0;
1207 }
1208 } else {
1209 member->energy_level = atoi((char *) data);
1210 }
1211 unlock_member(member);
1212 }
1213 if (stream != NULL) {
1214 stream->write_function(stream, "Energy %u = %d\n", member->id, member->energy_level);
1215 }
1216
1217 if (member->auto_energy_level && member->energy_level > member->auto_energy_level) {
1218 member->auto_energy_level = 0;
1219 if (stream != NULL) {
1220 stream->write_function(stream, "Auto-Energy level exceeded, Auto-Energy mode disabled\n", SWITCH_VA_NONE);
1221 }
1222 }
1223
1224
1225 if (test_eflag(member->conference, EFLAG_ENERGY_LEVEL_MEMBER) &&
1226 data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1227 conference_member_add_event_data(member, event);
1228 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "energy-level-member");
1229 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Energy-Level", "%d", member->energy_level);
1230 switch_event_fire(&event);
1231 }
1232
1233 return SWITCH_STATUS_SUCCESS;
1234 }
1235
conference_api_set_agc(conference_member_t * member,const char * data)1236 void conference_api_set_agc(conference_member_t *member, const char *data)
1237 {
1238 int tmp = 0;
1239 char *argv[4] = { 0 };
1240 char *conf;
1241
1242 if (data) {
1243 conf = switch_core_strdup(member->pool, data);
1244 switch_split(conf, ':', argv);
1245 } else {
1246 member->agc_level = member->conference->agc_level;
1247 member->agc_low_energy_level = member->conference->agc_low_energy_level;
1248 member->agc_change_factor = member->conference->agc_change_factor;
1249 member->agc_margin = member->conference->agc_margin;
1250 member->agc_period_len = member->conference->agc_period_len;
1251 }
1252
1253 if (argv[0]) {
1254 tmp = atoi(argv[0]);
1255
1256 if (tmp > 0) {
1257 member->agc_level = tmp;
1258 }
1259 }
1260
1261 if (argv[1]) {
1262 tmp = atoi(argv[1]);
1263
1264 if (tmp > 0) {
1265 member->agc_low_energy_level = tmp;
1266 }
1267 }
1268
1269
1270 if (argv[2]) {
1271 tmp = atoi(argv[2]);
1272
1273 if (tmp > 0) {
1274 member->agc_change_factor = tmp;
1275 }
1276 }
1277
1278
1279 if (argv[0]) {
1280 tmp = atoi(argv[0]);
1281
1282 if (tmp > 0) {
1283 member->agc_period_len = (1000 / member->conference->interval) * tmp;
1284 }
1285 }
1286
1287
1288 if (!member->agc) {
1289 switch_agc_create(&member->agc, member->agc_level, member->agc_low_energy_level, member->agc_margin,
1290 member->agc_change_factor, member->agc_period_len);
1291 switch_agc_set_token(member->agc, switch_channel_get_name(member->channel));
1292 } else {
1293 switch_agc_set(member->agc, member->agc_level, member->agc_low_energy_level, member->agc_margin,
1294 member->agc_change_factor, member->agc_period_len);
1295 }
1296
1297 }
1298
1299
conference_api_sub_agc(conference_member_t * member,switch_stream_handle_t * stream,void * data)1300 switch_status_t conference_api_sub_agc(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1301 {
1302 switch_event_t *event;
1303
1304 if (member == NULL) {
1305 return SWITCH_STATUS_GENERR;
1306 }
1307
1308 if (data) {
1309 lock_member(member);
1310 if (!strcasecmp(data, "up")) {
1311 member->agc_level += 200;
1312 if (member->agc_level > 1800) {
1313 member->agc_level = 1800;
1314 }
1315 } else if (!strcasecmp(data, "down")) {
1316 member->agc_level -= 200;
1317 if (member->agc_level < 0) {
1318 member->agc_level = 0;
1319 }
1320 } else {
1321 conference_api_set_agc(member, (char *)data);
1322 }
1323 unlock_member(member);
1324 }
1325 if (stream != NULL) {
1326 stream->write_function(stream, "Agc %u = %d\n", member->id, member->agc_level);
1327 }
1328
1329
1330 // if (test_eflag(member->conference, EFLAG_AGC_LEVEL_MEMBER) &&
1331 if (data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1332 conference_member_add_event_data(member, event);
1333 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "agc-level-member");
1334 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Agc-Level", "%d", member->agc_level);
1335 switch_event_fire(&event);
1336 }
1337
1338 return SWITCH_STATUS_SUCCESS;
1339 }
1340
1341
conference_api_sub_auto_energy(conference_member_t * member,switch_stream_handle_t * stream,void * data)1342 switch_status_t conference_api_sub_auto_energy(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1343 {
1344 switch_event_t *event;
1345
1346 if (member == NULL) {
1347 return SWITCH_STATUS_GENERR;
1348 }
1349
1350 if (data) {
1351 lock_member(member);
1352 if (!strcasecmp(data, "up")) {
1353 member->auto_energy_level += 200;
1354 if (member->auto_energy_level > 1800) {
1355 member->auto_energy_level = 1800;
1356 }
1357 } else if (!strcasecmp(data, "down")) {
1358 member->auto_energy_level -= 200;
1359 if (member->auto_energy_level < 0) {
1360 member->auto_energy_level = 0;
1361 }
1362 } else {
1363 member->auto_energy_level = atoi((char *) data);
1364 }
1365 unlock_member(member);
1366 }
1367 if (stream != NULL) {
1368 stream->write_function(stream, "%u = Auto-Energy: %d Energy: %d\n", member->id, member->auto_energy_level, member->energy_level);
1369 }
1370
1371 if (!member->energy_level) {
1372 member->energy_level = member->auto_energy_level / 2;
1373 }
1374
1375
1376 if (test_eflag(member->conference, EFLAG_ENERGY_LEVEL_MEMBER) &&
1377 data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1378 conference_member_add_event_data(member, event);
1379 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "auto-energy-level-member");
1380 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Auto-Energy-Level", "%d", member->auto_energy_level);
1381 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Energy-Level", "%d", member->energy_level);
1382 switch_event_fire(&event);
1383 }
1384
1385 return SWITCH_STATUS_SUCCESS;
1386 }
1387
conference_api_sub_max_energy(conference_member_t * member,switch_stream_handle_t * stream,void * data)1388 switch_status_t conference_api_sub_max_energy(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1389 {
1390 switch_event_t *event;
1391
1392 if (member == NULL) {
1393 return SWITCH_STATUS_GENERR;
1394 }
1395
1396 if (data) {
1397 lock_member(member);
1398 if (!strcasecmp(data, "up")) {
1399 member->max_energy_level += 200;
1400 if (member->max_energy_level > 1800) {
1401 member->max_energy_level = 1800;
1402 }
1403 } else if (!strcasecmp(data, "down")) {
1404 member->max_energy_level -= 200;
1405 if (member->max_energy_level < 0) {
1406 member->max_energy_level = 0;
1407 }
1408 } else {
1409 member->max_energy_level = atoi((char *) data);
1410 }
1411 unlock_member(member);
1412 }
1413
1414 if (member->max_energy_level && member->max_energy_level < member->energy_level) {
1415 member->max_energy_level = 0;
1416 if (stream != NULL) stream->write_function(stream, "-ERR %u Max-Energy cannot exceed energy level.\n", member->id);
1417 } else if (data) {
1418 char *p, *q;
1419 if ((p = strchr(data, ':'))) {
1420 p++;
1421 if (*p) {
1422 int tmp = atoi(p);
1423 if (tmp >= 0) {
1424 member->burst_mute_count = tmp / member->conference->interval;
1425 }
1426
1427 if ((q = strchr(p, ':'))) {
1428 q++;
1429 if (*q) {
1430 int tmp = atoi(q);
1431
1432 if (tmp >= 0) {
1433 member->max_energy_hit_trigger = tmp;
1434 }
1435 }
1436 }
1437 }
1438 }
1439 }
1440
1441
1442 if (stream != NULL) {
1443 stream->write_function(stream, "%u = Max-Energy: %d Energy: %d Max-Energy-Mute: %dms Max-Energy-Hit-Trigger %d\n",
1444 member->id, member->energy_level, member->max_energy_level, member->burst_mute_count * member->conference->interval, member->max_energy_hit_trigger);
1445 }
1446
1447
1448 if (test_eflag(member->conference, EFLAG_ENERGY_LEVEL_MEMBER) &&
1449 data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1450 conference_member_add_event_data(member, event);
1451 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "max-energy-level-member");
1452 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Max-Energy-Level", "%d", member->max_energy_level);
1453 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Max-Energy-Mute", "%d", member->burst_mute_count * member->conference->interval);
1454 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Max-Energy-Hit-Trigger", "%d", member->max_energy_hit_trigger);
1455 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Energy-Level", "%d", member->max_energy_level);
1456
1457 switch_event_fire(&event);
1458 }
1459
1460 return SWITCH_STATUS_SUCCESS;
1461 }
1462
conference_api_sub_auto_position(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1463 switch_status_t conference_api_sub_auto_position(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1464 {
1465 #ifdef OPENAL_POSITIONING
1466 char *arg = NULL;
1467 int set = 0;
1468
1469 if (argc > 2) {
1470 arg = argv[2];
1471 }
1472
1473
1474 if (!zstr(arg)) {
1475 if (!strcasecmp(arg, "on")) {
1476 conference_utils_set_flag(conference, CFLAG_POSITIONAL);
1477 set = 1;
1478 } else if (!strcasecmp(arg, "off")) {
1479 conference_utils_clear_flag(conference, CFLAG_POSITIONAL);
1480 }
1481 }
1482
1483 if (set && conference_utils_test_flag(conference, CFLAG_POSITIONAL)) {
1484 conference_al_gen_arc(conference, stream);
1485 }
1486
1487 stream->write_function(stream, "+OK positioning %s\n", conference_utils_test_flag(conference, CFLAG_POSITIONAL) ? "on" : "off");
1488
1489 #else
1490 stream->write_function(stream, "-ERR not supported\n");
1491
1492 #endif
1493
1494 return SWITCH_STATUS_SUCCESS;
1495 }
1496
conference_api_sub_position(conference_member_t * member,switch_stream_handle_t * stream,void * data)1497 switch_status_t conference_api_sub_position(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1498 {
1499 #ifndef OPENAL_POSITIONING
1500 if (stream) stream->write_function(stream, "-ERR not supported\n");
1501 #else
1502 switch_event_t *event;
1503
1504 if (member == NULL) {
1505 return SWITCH_STATUS_GENERR;
1506 }
1507
1508 if (conference_utils_member_test_flag(member, MFLAG_NO_POSITIONAL)) {
1509 if (stream) stream->write_function(stream,
1510 "-ERR %s has positional audio blocked.\n", switch_channel_get_name(member->channel));
1511 return SWITCH_STATUS_SUCCESS;
1512 }
1513
1514 if (!member->al) {
1515 if (!conference_utils_member_test_flag(member, MFLAG_POSITIONAL) && member->conference->channels == 2) {
1516 conference_utils_member_set_flag(member, MFLAG_POSITIONAL);
1517 member->al = conference_al_create(member->pool);
1518 } else {
1519
1520 if (stream) {
1521 stream->write_function(stream, "-ERR Positional audio not avalilable %d\n", member->conference->channels);
1522 }
1523 return SWITCH_STATUS_FALSE;
1524 }
1525 }
1526
1527
1528 if (data) {
1529 if (conference_member_parse_position(member, data) != SWITCH_STATUS_SUCCESS) {
1530 if (stream) {
1531 stream->write_function(stream, "-ERR invalid input!\n");
1532 }
1533 return SWITCH_STATUS_FALSE;
1534 }
1535 }
1536
1537
1538 if (stream != NULL) {
1539 stream->write_function(stream, "+OK Position %u = %0.2f:%0.2f:%0.2f\n", member->id, member->al->pos_x, member->al->pos_y, member->al->pos_z);
1540 }
1541
1542 if (test_eflag(member->conference, EFLAG_SET_POSITION_MEMBER) &&
1543 data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1544 conference_member_add_event_data(member, event);
1545 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "set-position-member");
1546 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Position", "%0.2f:%0.2f:%0.2f", member->al->pos_x, member->al->pos_y, member->al->pos_z);
1547 switch_event_fire(&event);
1548 }
1549
1550 #endif
1551
1552 return SWITCH_STATUS_SUCCESS;
1553 }
1554
conference_api_sub_volume_in(conference_member_t * member,switch_stream_handle_t * stream,void * data)1555 switch_status_t conference_api_sub_volume_in(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1556 {
1557 switch_event_t *event;
1558
1559 if (member == NULL)
1560 return SWITCH_STATUS_GENERR;
1561
1562 if (data) {
1563 lock_member(member);
1564 if (!strcasecmp(data, "up")) {
1565 member->volume_in_level++;
1566 switch_normalize_volume(member->volume_in_level);
1567 } else if (!strcasecmp(data, "down")) {
1568 member->volume_in_level--;
1569 switch_normalize_volume(member->volume_in_level);
1570 } else {
1571 member->volume_in_level = atoi((char *) data);
1572 switch_normalize_volume(member->volume_in_level);
1573 }
1574 unlock_member(member);
1575
1576 }
1577 if (stream != NULL) {
1578 stream->write_function(stream, "+OK Volume IN %u = %d\n", member->id, member->volume_in_level);
1579 }
1580 if (test_eflag(member->conference, EFLAG_VOLUME_IN_MEMBER) &&
1581 data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1582 conference_member_add_event_data(member, event);
1583 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "volume-in-member");
1584 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Volume-Level", "%d", member->volume_in_level);
1585 switch_event_fire(&event);
1586 }
1587
1588 return SWITCH_STATUS_SUCCESS;
1589 }
1590
conference_api_sub_volume_out(conference_member_t * member,switch_stream_handle_t * stream,void * data)1591 switch_status_t conference_api_sub_volume_out(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1592 {
1593 switch_event_t *event;
1594
1595 if (member == NULL)
1596 return SWITCH_STATUS_GENERR;
1597
1598 if (data) {
1599 lock_member(member);
1600 if (!strcasecmp(data, "up")) {
1601 member->volume_out_level++;
1602 switch_normalize_volume(member->volume_out_level);
1603 } else if (!strcasecmp(data, "down")) {
1604 member->volume_out_level--;
1605 switch_normalize_volume(member->volume_out_level);
1606 } else {
1607 member->volume_out_level = atoi((char *) data);
1608 switch_normalize_volume(member->volume_out_level);
1609 }
1610 unlock_member(member);
1611 }
1612 if (stream != NULL) {
1613 stream->write_function(stream, "+OK Volume OUT %u = %d\n", member->id, member->volume_out_level);
1614 }
1615 if (test_eflag(member->conference, EFLAG_VOLUME_OUT_MEMBER) && data &&
1616 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1617 conference_member_add_event_data(member, event);
1618 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "volume-out-member");
1619 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Volume-Level", "%d", member->volume_out_level);
1620 switch_event_fire(&event);
1621 }
1622
1623 return SWITCH_STATUS_SUCCESS;
1624 }
1625
conference_api_sub_vid_personal(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1626 switch_status_t conference_api_sub_vid_personal(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1627 {
1628 int on = 0;
1629
1630 if (!conference->canvases[0]) {
1631 stream->write_function(stream, "-ERR conference is not in mixing mode\n");
1632 return SWITCH_STATUS_SUCCESS;
1633 }
1634
1635 if (argv[2]) {
1636 on = switch_true(argv[2]);
1637 if (on) {
1638 conference_utils_set_flag(conference, CFLAG_PERSONAL_CANVAS);
1639 conference->video_layout_group = "grid";
1640 conference_utils_set_flag(conference, CFLAG_REFRESH_LAYOUT);
1641 } else {
1642 conference_utils_clear_flag(conference, CFLAG_PERSONAL_CANVAS);
1643 }
1644 }
1645
1646 stream->write_function(stream, "+OK personal is %s\n", on ? "on" : "off");
1647
1648 return SWITCH_STATUS_SUCCESS;
1649 }
1650
conference_api_sub_vid_bandwidth(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1651 switch_status_t conference_api_sub_vid_bandwidth(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1652 {
1653 uint32_t i;
1654 int32_t video_write_bandwidth;
1655 int x = 0, id = -1;
1656 char *group = NULL;
1657 char *array[4] = {0};
1658 float sdiv = 0;
1659 int fdiv = 0;
1660 int force_w = 0, force_h = 0;
1661 conference_member_t *imember;
1662
1663 if (!argv[2]) {
1664 stream->write_function(stream, "-ERR Invalid input\n");
1665 return SWITCH_STATUS_SUCCESS;
1666 }
1667
1668 switch_split(argv[2], ':', array);
1669
1670 if (array[1]) {
1671 if (*array[1] == '=') {
1672 char *p = array[1];
1673
1674 force_w = atoi((p+1));
1675 if ((p = strchr(p+1, 'x'))) {
1676 p++;
1677 if (*p) {
1678 force_h = atoi(p);
1679 }
1680 }
1681
1682 if (!(force_w > 100 && force_w < 1920 && force_h > 100 && force_h < 1080)) {
1683 force_w = force_h = 0;
1684 }
1685 } else {
1686 sdiv = atof(array[1]);
1687 if (sdiv < 1.5 || sdiv > 8.0) {
1688 sdiv = 0;
1689 }
1690 }
1691 }
1692
1693 if (array[2]) {
1694 fdiv = atoi(array[2]);
1695 if (fdiv < 2 || fdiv > 8) {
1696 fdiv = 0;
1697 }
1698 }
1699
1700 video_write_bandwidth = switch_parse_bandwidth_string(array[0]);
1701
1702 if (argv[3]) {
1703 group = argv[3];
1704 }
1705
1706 if (argv[4]) {
1707 id = atoi(argv[4]);
1708
1709 if (id < 1 || id > MAX_CANVASES+1) {
1710 id = -1;
1711 }
1712
1713 if (id < 1 || id > conference->canvas_count) {
1714 stream->write_function(stream, "-ERR Invalid canvas\n");
1715 goto end;
1716 }
1717 }
1718
1719 switch_mutex_lock(conference->member_mutex);
1720
1721 for (imember = conference->members; imember; imember = imember->next) {
1722
1723 if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO_READY)) {
1724 continue;
1725 }
1726
1727 switch_core_media_set_outgoing_bitrate(imember->session, SWITCH_MEDIA_TYPE_VIDEO, video_write_bandwidth);
1728
1729 stream->write_function(stream, "+OK Set Bandwidth %d kps for member %s\n", video_write_bandwidth, switch_channel_get_name(imember->channel));
1730 }
1731
1732 for (i = 0; i <= conference->canvas_count; i++) {
1733 if (i != id - 1) {
1734 continue;
1735 }
1736
1737 if (conference->canvases[i]) {
1738 mcu_canvas_t *canvas = conference->canvases[i];
1739 int j;
1740
1741 for (j = 0; j < canvas->write_codecs_count; j++) {
1742 int w = canvas->width, h = canvas->height;
1743
1744 if ((zstr(group) || !strcmp(group, switch_str_nil(canvas->write_codecs[j]->video_codec_group)))) {
1745 switch_core_codec_control(&canvas->write_codecs[j]->codec, SCC_VIDEO_BANDWIDTH,
1746 SCCT_INT, &video_write_bandwidth, SCCT_NONE, NULL, NULL, NULL);
1747
1748 if (fdiv) {
1749 canvas->write_codecs[j]->fps_divisor = fdiv;
1750 }
1751
1752 if (force_w && force_h) {
1753 w = force_w;
1754 h = force_h;
1755 } else if (sdiv) {
1756 w = (int)((float) w / sdiv);
1757 h = (int)((float) h / sdiv);
1758 }
1759
1760 if (w && h) {
1761 switch_img_free(&canvas->write_codecs[j]->scaled_img);
1762 if (w != canvas->img->d_w || h != canvas->img->d_h) {
1763 canvas->write_codecs[j]->scaled_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, w, h, 16);
1764 }
1765 }
1766
1767 if (!sdiv && w) {
1768 sdiv = (float)canvas->img->d_w / w;
1769 }
1770
1771
1772 stream->write_function(stream, "+OK Set Bandwidth for canvas %d index %d group[%s] to %d sdiv %.2f %dx%d fdiv %d\n", i + 1, j,
1773 switch_str_nil(canvas->write_codecs[j]->video_codec_group), video_write_bandwidth, sdiv, w,h, fdiv);
1774
1775
1776 x++;
1777 }
1778 }
1779 }
1780 }
1781 switch_mutex_unlock(conference->member_mutex);
1782
1783 end:
1784
1785 if (!x) {
1786 stream->write_function(stream, "-ERR Bandwidth not set\n");
1787 }
1788
1789 return SWITCH_STATUS_SUCCESS;
1790 }
1791
1792
conference_api_sub_canvas_bgimg(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1793 switch_status_t conference_api_sub_canvas_bgimg(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1794 {
1795 mcu_canvas_t *canvas = NULL;
1796 int idx = 0;
1797 char *file = NULL;
1798 switch_status_t status = SWITCH_STATUS_FALSE;
1799
1800 if (!argv[2]) {
1801 stream->write_function(stream, "-ERR Invalid input\n");
1802 return SWITCH_STATUS_SUCCESS;
1803 }
1804
1805 file = argv[2];
1806
1807 if (argv[3]) {
1808 idx = atoi(argv[3]) - 1;
1809 }
1810
1811 if (idx < 0 || idx > SUPER_CANVAS_ID || !conference->canvases[idx]) {
1812 stream->write_function(stream, "-ERR Invalid canvas\n");
1813 return SWITCH_STATUS_SUCCESS;
1814 }
1815
1816 canvas = conference->canvases[idx];
1817 switch_mutex_lock(canvas->mutex);
1818 if (!strcasecmp(file, "clear")) {
1819 conference_video_reset_image(canvas->img, &canvas->bgcolor);
1820 } else {
1821 status = conference_video_set_canvas_bgimg(canvas, file);
1822 }
1823 switch_mutex_unlock(canvas->mutex);
1824
1825 if (status == SWITCH_STATUS_SUCCESS) {
1826 stream->write_function(stream, "+OK Set Bgimg %s\n", file);
1827 } else {
1828 stream->write_function(stream, "-ERR Error Setting Bgimg %s\n", file);
1829 }
1830
1831 return SWITCH_STATUS_SUCCESS;
1832 }
1833
1834
conference_api_sub_canvas_fgimg(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1835 switch_status_t conference_api_sub_canvas_fgimg(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1836 {
1837 mcu_canvas_t *canvas = NULL;
1838 int idx = 0;
1839 char *file = NULL;
1840 switch_status_t status = SWITCH_STATUS_FALSE;
1841
1842 if (!argv[2]) {
1843 stream->write_function(stream, "-ERR Invalid input\n");
1844 return SWITCH_STATUS_SUCCESS;
1845 }
1846
1847 file = argv[2];
1848
1849 if (argv[3]) {
1850 idx = atoi(argv[3]) - 1;
1851 }
1852
1853 if (idx < 0 || idx > SUPER_CANVAS_ID || !conference->canvases[idx]) {
1854 stream->write_function(stream, "-ERR Invalid canvas\n");
1855 return SWITCH_STATUS_SUCCESS;
1856 }
1857
1858 if ((canvas = conference->canvases[idx])) {
1859 switch_mutex_lock(canvas->mutex);
1860 if (!strcasecmp(file, "clear")) {
1861 conference_video_reset_image(canvas->img, &canvas->bgcolor);
1862 } else {
1863 status = conference_video_set_canvas_fgimg(canvas, file);
1864 }
1865 switch_mutex_unlock(canvas->mutex);
1866 }
1867
1868 if (status == SWITCH_STATUS_SUCCESS) {
1869 stream->write_function(stream, "+OK Set FGimg %s\n", file);
1870 } else {
1871 stream->write_function(stream, "-ERR Error Setting FGimg %s\n", file);
1872 }
1873
1874 return SWITCH_STATUS_SUCCESS;
1875 }
1876
conference_api_sub_vid_res(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1877 switch_status_t conference_api_sub_vid_res(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1878 {
1879 int canvas_w = 0, canvas_h = 0, id = 0;
1880 char *video_canvas_size = argv[2];
1881
1882
1883 if (!conference->canvases[0]) {
1884 stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
1885 return SWITCH_STATUS_SUCCESS;
1886 }
1887
1888 if (zstr(video_canvas_size)) {
1889 stream->write_function(stream, "-ERR Invalid size\n");
1890 return SWITCH_STATUS_SUCCESS;
1891 } else {
1892 char *p;
1893
1894 if ((canvas_w = atoi(video_canvas_size))) {
1895 if ((p = strchr(video_canvas_size, 'x'))) {
1896 p++;
1897 if (*p) {
1898 canvas_h = atoi(p);
1899 }
1900 }
1901 }
1902 }
1903
1904 if (canvas_w < 320 || canvas_h < 180) {
1905 stream->write_function(stream, "-ERR Invalid size, [%dx%d] is too small\n", canvas_w, canvas_h);
1906 return SWITCH_STATUS_SUCCESS;
1907 }
1908
1909 if (canvas_w > 7680 || canvas_h > 4320) {
1910 stream->write_function(stream, "-ERR Invalid size, [%dx%d] is too large.\n", canvas_w, canvas_h);
1911 return SWITCH_STATUS_SUCCESS;
1912 }
1913
1914
1915 if (argv[3]) {
1916
1917 id = atoi(argv[3]);
1918
1919 if (id < 1 || id > MAX_CANVASES+1) {
1920 id = -1;
1921 }
1922
1923 if (id < 1) {
1924 stream->write_function(stream, "-ERR Invalid canvas\n");
1925 }
1926
1927 }
1928
1929 if (id == 0) id = 1;
1930
1931 if (id > conference->canvas_count + 1) {
1932 id = 1;
1933 }
1934
1935 if (conference_video_change_res(conference, canvas_w, canvas_h, id - 1) == SWITCH_STATUS_SUCCESS) {
1936 stream->write_function(stream, "+OK Resolution set to [%dx%d]\n", canvas_w, canvas_h);
1937 } else {
1938 stream->write_function(stream, "-ERR Resolution not set\n");
1939 }
1940
1941 return SWITCH_STATUS_SUCCESS;
1942 }
1943
conference_api_sub_vid_fps(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1944 switch_status_t conference_api_sub_vid_fps(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1945 {
1946 float fps = 0;
1947
1948 if (!conference->canvases[0]) {
1949 stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
1950 return SWITCH_STATUS_SUCCESS;
1951 }
1952
1953 if (!argv[2]) {
1954 stream->write_function(stream, "+OK Current FPS [%0.2f]\n", conference->video_fps.fps);
1955 return SWITCH_STATUS_SUCCESS;
1956 }
1957
1958 fps = (float)atof(argv[2]);
1959
1960 if (conference_video_set_fps(conference, fps)) {
1961 stream->write_function(stream, "+OK FPS set to [%0.2f]\n", conference->video_fps.fps);
1962 } else {
1963 stream->write_function(stream, "-ERR Invalid FPS [%s]\n", argv[2]);
1964 }
1965
1966 return SWITCH_STATUS_SUCCESS;
1967
1968 }
1969
conference_api_sub_write_png(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1970 switch_status_t conference_api_sub_write_png(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1971 {
1972 switch_status_t status = SWITCH_STATUS_FALSE;
1973 mcu_canvas_t *canvas = NULL;
1974
1975 if (!argv[2]) {
1976 stream->write_function(stream, "-ERR Invalid input\n");
1977 return SWITCH_STATUS_SUCCESS;
1978 }
1979
1980 if (!conference->canvas_count) {
1981 stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
1982 return SWITCH_STATUS_SUCCESS;
1983 }
1984
1985 if (conference->canvas_count > 1) {
1986 /* pick super canvas */
1987 canvas = conference->canvases[conference->canvas_count];
1988 } else {
1989 canvas = conference->canvases[0];
1990 }
1991
1992 switch_mutex_lock(canvas->mutex);
1993 status = switch_img_write_png(canvas->img, argv[2]);
1994 switch_mutex_unlock(canvas->mutex);
1995
1996 stream->write_function(stream, "%s\n", status == SWITCH_STATUS_SUCCESS ? "+OK" : "-ERR");
1997
1998 return SWITCH_STATUS_SUCCESS;
1999 }
2000
conference_api_sub_vid_layout(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2001 switch_status_t conference_api_sub_vid_layout(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2002 {
2003 video_layout_t *vlayout = NULL;
2004 char *group_name = NULL;
2005
2006 int idx = 0;
2007
2008 if (!argv[2]) {
2009 stream->write_function(stream, "-ERR Invalid input\n");
2010 return SWITCH_STATUS_SUCCESS;
2011 }
2012
2013 if (!conference->canvases[0]) {
2014 stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
2015 return SWITCH_STATUS_SUCCESS;
2016 }
2017
2018 if (!strcasecmp(argv[2], "list")) {
2019 switch_hash_index_t *hi;
2020 void *val;
2021 const void *vvar;
2022 for (hi = switch_core_hash_first(conference->layout_hash); hi; hi = switch_core_hash_next(&hi)) {
2023 switch_core_hash_this(hi, &vvar, NULL, &val);
2024 stream->write_function(stream, "%s\n", (char *)vvar);
2025 }
2026 return SWITCH_STATUS_SUCCESS;
2027 }
2028
2029 if (!strncasecmp(argv[2], "group", 5)) {
2030 layout_group_t *lg = NULL;
2031 int xx = 4;
2032
2033 if ((group_name = strchr(argv[2], ':'))) {
2034 group_name++;
2035 xx--;
2036 } else {
2037 group_name = argv[3];
2038 }
2039
2040 if (!group_name) {
2041 stream->write_function(stream, "-ERR Group name not specified.\n");
2042 return SWITCH_STATUS_SUCCESS;
2043 } else {
2044 if (((lg = switch_core_hash_find(conference->layout_group_hash, group_name)))) {
2045 if (conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS)) {
2046 stream->write_function(stream, "-ERR Change personal canvas to layout group [%s]\n", group_name);
2047 conference->video_layout_group = switch_core_strdup(conference->pool, group_name);
2048 conference_utils_set_flag(conference, CFLAG_REFRESH_LAYOUT);
2049 return SWITCH_STATUS_SUCCESS;
2050 }
2051 } else {
2052 group_name = NULL;
2053 }
2054
2055 stream->write_function(stream, "+OK Change to layout group [%s]\n", group_name);
2056
2057 if (argv[xx]) {
2058 if ((idx = atoi(argv[xx])) > 0) {
2059 idx--;
2060 }
2061 }
2062 }
2063 }
2064
2065 if ((vlayout = switch_core_hash_find(conference->layout_hash, argv[2]))) {
2066 if (argv[3]) {
2067 if ((idx = atoi(argv[3]))) {
2068 idx--;
2069 }
2070 }
2071 }
2072
2073 if (!vlayout && !group_name) {
2074 stream->write_function(stream, "-ERR Invalid layout [%s]\n", argv[2]);
2075 return SWITCH_STATUS_SUCCESS;
2076 }
2077
2078 if (idx < 0 || idx > (int)(conference->canvas_count - 1)) idx = 0;
2079
2080
2081 if (conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS)) {
2082 if (vlayout) {
2083 stream->write_function(stream, "+OK Change personal canvas set to layout [%s]\n", vlayout->name);
2084 switch_mutex_lock(conference->member_mutex);
2085 conference->new_personal_vlayout = vlayout;
2086 switch_mutex_unlock(conference->member_mutex);
2087 } else {
2088 stream->write_function(stream, "-ERR no layout for personal canvas\n");
2089 return SWITCH_STATUS_SUCCESS;
2090 }
2091 } else {
2092
2093 switch_mutex_lock(conference->canvases[idx]->mutex);
2094 if (vlayout) {
2095 stream->write_function(stream, "+OK Change canvas %d to layout [%s]\n", idx + 1, vlayout->name);
2096 conference->canvases[idx]->new_vlayout = vlayout;
2097 conference->canvases[idx]->video_layout_group = NULL;
2098 } else if (group_name) {
2099 conference->canvases[idx]->video_layout_group = switch_core_strdup(conference->pool, group_name);
2100 conference_utils_set_flag(conference, CFLAG_REFRESH_LAYOUT);
2101 }
2102 switch_mutex_unlock(conference->canvases[idx]->mutex);
2103 }
2104
2105 return SWITCH_STATUS_SUCCESS;
2106 }
2107
conference_api_sub_count(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2108 switch_status_t conference_api_sub_count(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2109 {
2110
2111 if (conference) {
2112 conference_list_count_only(conference, stream);
2113 } else {
2114 int count = 0;
2115 switch_hash_index_t *hi;
2116 switch_mutex_lock(conference_globals.hash_mutex);
2117 for (hi = switch_core_hash_first(conference_globals.conference_hash); hi; hi = switch_core_hash_next(&hi)) {
2118 count++;
2119 }
2120 switch_mutex_unlock(conference_globals.hash_mutex);
2121 stream->write_function(stream, "%d", count);
2122 }
2123
2124 return SWITCH_STATUS_SUCCESS;
2125 }
2126
conference_api_sub_list(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2127 switch_status_t conference_api_sub_list(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2128 {
2129 int ret_status = SWITCH_STATUS_GENERR;
2130 int count = 0;
2131 switch_hash_index_t *hi;
2132 void *val;
2133 char *d = ";";
2134 int pretty = 0;
2135 int summary = 0;
2136 int countonly = 0;
2137 int argofs = (argc >= 2 && strcasecmp(argv[1], "list") == 0); /* detect being called from chat vs. api */
2138
2139 if (argv[1 + argofs]) {
2140 if (argv[2 + argofs] && !strcasecmp(argv[1 + argofs], "delim")) {
2141 d = argv[2 + argofs];
2142
2143 if (*d == '"') {
2144 if (++d) {
2145 char *p;
2146 if ((p = strchr(d, '"'))) {
2147 *p = '\0';
2148 }
2149 } else {
2150 d = ";";
2151 }
2152 }
2153 } else if (strcasecmp(argv[1 + argofs], "pretty") == 0) {
2154 pretty = 1;
2155 } else if (strcasecmp(argv[1 + argofs], "summary") == 0) {
2156 summary = 1;
2157 } else if (strcasecmp(argv[1 + argofs], "count") == 0) {
2158 countonly = 1;
2159 }
2160 }
2161
2162 if (conference == NULL) {
2163 switch_mutex_lock(conference_globals.hash_mutex);
2164 for (hi = switch_core_hash_first(conference_globals.conference_hash); hi; hi = switch_core_hash_next(&hi)) {
2165 int fcount = 0;
2166 switch_core_hash_this(hi, NULL, NULL, &val);
2167 conference = (conference_obj_t *) val;
2168
2169 stream->write_function(stream, "+OK Conference %s (%u member%s rate: %u%s flags: ",
2170 conference->name,
2171 conference->count,
2172 conference->count == 1 ? "" : "s", conference->rate, conference_utils_test_flag(conference, CFLAG_LOCKED) ? " locked" : "");
2173
2174 if (conference_utils_test_flag(conference, CFLAG_LOCKED)) {
2175 stream->write_function(stream, "%slocked", fcount ? "|" : "");
2176 fcount++;
2177 }
2178
2179 if (conference_utils_test_flag(conference, CFLAG_DESTRUCT)) {
2180 stream->write_function(stream, "%sdestruct", fcount ? "|" : "");
2181 fcount++;
2182 }
2183
2184 if (conference_utils_test_flag(conference, CFLAG_WAIT_MOD)) {
2185 stream->write_function(stream, "%swait_mod", fcount ? "|" : "");
2186 fcount++;
2187 }
2188
2189 if (conference_utils_test_flag(conference, CFLAG_AUDIO_ALWAYS)) {
2190 stream->write_function(stream, "%saudio_always", fcount ? "|" : "");
2191 fcount++;
2192 }
2193
2194 if (conference_utils_test_flag(conference, CFLAG_RUNNING)) {
2195 stream->write_function(stream, "%srunning", fcount ? "|" : "");
2196 fcount++;
2197 }
2198
2199 if (conference_utils_test_flag(conference, CFLAG_ANSWERED)) {
2200 stream->write_function(stream, "%sanswered", fcount ? "|" : "");
2201 fcount++;
2202 }
2203
2204 if (conference_utils_test_flag(conference, CFLAG_ENFORCE_MIN)) {
2205 stream->write_function(stream, "%senforce_min", fcount ? "|" : "");
2206 fcount++;
2207 }
2208
2209 if (conference_utils_test_flag(conference, CFLAG_BRIDGE_TO)) {
2210 stream->write_function(stream, "%sbridge_to", fcount ? "|" : "");
2211 fcount++;
2212 }
2213
2214 if (conference_utils_test_flag(conference, CFLAG_DYNAMIC)) {
2215 stream->write_function(stream, "%sdynamic", fcount ? "|" : "");
2216 fcount++;
2217 }
2218
2219 if (conference_utils_test_flag(conference, CFLAG_EXIT_SOUND)) {
2220 stream->write_function(stream, "%sexit_sound", fcount ? "|" : "");
2221 fcount++;
2222 }
2223
2224 if (conference_utils_test_flag(conference, CFLAG_ENTER_SOUND)) {
2225 stream->write_function(stream, "%senter_sound", fcount ? "|" : "");
2226 fcount++;
2227 }
2228
2229 if (conference->record_count > 0) {
2230 stream->write_function(stream, "%srecording", fcount ? "|" : "");
2231 fcount++;
2232 }
2233
2234 if (conference_utils_test_flag(conference, CFLAG_VID_FLOOR)) {
2235 stream->write_function(stream, "%svideo_floor_only", fcount ? "|" : "");
2236 fcount++;
2237 }
2238
2239 if (conference_utils_test_flag(conference, CFLAG_RFC4579)) {
2240 stream->write_function(stream, "%svideo_rfc4579", fcount ? "|" : "");
2241 fcount++;
2242 }
2243
2244 if (conference_utils_test_flag(conference, CFLAG_LIVEARRAY_SYNC)) {
2245 stream->write_function(stream, "%slivearray_sync", fcount ? "|" : "");
2246 fcount++;
2247 }
2248
2249 if (conference_utils_test_flag(conference, CFLAG_VID_FLOOR_LOCK)) {
2250 stream->write_function(stream, "%svideo_floor_lock", fcount ? "|" : "");
2251 fcount++;
2252 }
2253
2254 if (conference_utils_test_flag(conference, CFLAG_TRANSCODE_VIDEO)) {
2255 stream->write_function(stream, "%stranscode_video", fcount ? "|" : "");
2256 fcount++;
2257 }
2258
2259 if (conference_utils_test_flag(conference, CFLAG_VIDEO_MUXING)) {
2260 stream->write_function(stream, "%svideo_muxing", fcount ? "|" : "");
2261 fcount++;
2262 }
2263
2264 if (conference_utils_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING)) {
2265 stream->write_function(stream, "%sminimize_video_encoding", fcount ? "|" : "");
2266 fcount++;
2267 }
2268
2269 if (conference_utils_test_flag(conference, CFLAG_MANAGE_INBOUND_VIDEO_BITRATE)) {
2270 stream->write_function(stream, "%smanage_inbound_bitrate", fcount ? "|" : "");
2271 fcount++;
2272 }
2273
2274 if (conference_utils_test_flag(conference, CFLAG_JSON_STATUS)) {
2275 stream->write_function(stream, "%sjson_status", fcount ? "|" : "");
2276 fcount++;
2277 }
2278
2279 if (conference_utils_test_flag(conference, CFLAG_VIDEO_BRIDGE_FIRST_TWO)) {
2280 stream->write_function(stream, "%svideo_bridge_first_two", fcount ? "|" : "");
2281 fcount++;
2282 }
2283
2284 if (conference_utils_test_flag(conference, CFLAG_VIDEO_REQUIRED_FOR_CANVAS)) {
2285 stream->write_function(stream, "%svideo_required_for_canvas", fcount ? "|" : "");
2286 fcount++;
2287 }
2288
2289 if (conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS)) {
2290 stream->write_function(stream, "%spersonal_canvas", fcount ? "|" : "");
2291 fcount++;
2292 }
2293
2294 if (!fcount) {
2295 stream->write_function(stream, "none");
2296 }
2297
2298 stream->write_function(stream, ")\n");
2299
2300 count++;
2301 if (!summary) {
2302 if (pretty) {
2303 conference_list_pretty(conference, stream);
2304 } else {
2305 conference_list(conference, stream, d);
2306 }
2307 }
2308 }
2309 switch_mutex_unlock(conference_globals.hash_mutex);
2310 } else {
2311 count++;
2312 if (countonly) {
2313 conference_list_count_only(conference, stream);
2314 } else if (pretty) {
2315 conference_list_pretty(conference, stream);
2316 } else {
2317 conference_list(conference, stream, d);
2318 }
2319 }
2320
2321 if (!count) {
2322 stream->write_function(stream, "+OK No active conferences.\n");
2323 }
2324
2325 ret_status = SWITCH_STATUS_SUCCESS;
2326
2327 return ret_status;
2328 }
2329
conference_api_sub_floor(conference_member_t * member,switch_stream_handle_t * stream,void * data)2330 switch_status_t conference_api_sub_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2331 {
2332
2333 if (member == NULL)
2334 return SWITCH_STATUS_GENERR;
2335
2336 if (conference_utils_member_test_flag(member, MFLAG_DED_VID_LAYER) && !conference_utils_test_flag(member->conference, CFLAG_DED_VID_LAYER_AUDIO_FLOOR)) {
2337 if (stream != NULL) {
2338 stream->write_function(stream, "-ERR cannot set floor on a member in an active video role\n");
2339 }
2340
2341 return SWITCH_STATUS_SUCCESS;
2342 }
2343
2344 if (member->conference->floor_holder == member->id) {
2345 conference_member_set_floor_holder(member->conference, NULL, 0);
2346 if (stream != NULL) {
2347 stream->write_function(stream, "+OK floor none\n");
2348 }
2349 } else if (member->conference->floor_holder == 0) {
2350 conference_member_set_floor_holder(member->conference, member, 0);
2351 if (stream != NULL) {
2352 stream->write_function(stream, "+OK floor %u\n", member->id);
2353 }
2354 } else {
2355 if (stream != NULL) {
2356 stream->write_function(stream, "-ERR floor is held by %u\n", member->conference->floor_holder);
2357 }
2358 }
2359
2360 return SWITCH_STATUS_SUCCESS;
2361 }
2362
conference_api_sub_clear_vid_floor(conference_obj_t * conference,switch_stream_handle_t * stream,void * data)2363 switch_status_t conference_api_sub_clear_vid_floor(conference_obj_t *conference, switch_stream_handle_t *stream, void *data)
2364 {
2365
2366 switch_mutex_lock(conference->mutex);
2367 conference_utils_clear_flag(conference, CFLAG_VID_FLOOR_LOCK);
2368 //conference_video_set_floor_holder(conference, NULL);
2369 switch_mutex_unlock(conference->mutex);
2370
2371 stream->write_function(stream, "+OK floor Cleared\n", SWITCH_VA_NONE);
2372
2373 return SWITCH_STATUS_SUCCESS;
2374 }
2375
conference_api_sub_vid_mute_img(conference_member_t * member,switch_stream_handle_t * stream,void * data)2376 switch_status_t conference_api_sub_vid_mute_img(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2377 {
2378 char *text = (char *) data;
2379 mcu_layer_t *layer = NULL;
2380
2381 if (member == NULL)
2382 return SWITCH_STATUS_GENERR;
2383
2384 if (!switch_channel_test_flag(member->channel, CF_VIDEO)) {
2385 return SWITCH_STATUS_FALSE;
2386 }
2387
2388 layer = conference_video_get_layer_locked(member);
2389
2390 if (!layer) {
2391 goto end;
2392 }
2393
2394 member->video_mute_png = NULL;
2395
2396 if (text) {
2397 switch_img_free(&layer->mute_img);
2398 }
2399
2400 if (text && strcasecmp(text, "clear")) {
2401 member->video_mute_png = switch_core_strdup(member->pool, text);
2402 }
2403
2404 end:
2405
2406 stream->write_function(stream, "%s\n", member->video_mute_png ? member->video_mute_png : "_undef_");
2407
2408 conference_video_release_layer(&layer);
2409
2410 return SWITCH_STATUS_SUCCESS;
2411
2412 }
2413
2414
conference_api_sub_vid_logo_img(conference_member_t * member,switch_stream_handle_t * stream,void * data)2415 switch_status_t conference_api_sub_vid_logo_img(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2416 {
2417 char *text = (char *) data;
2418 mcu_layer_t *layer = NULL;
2419
2420 if (member == NULL)
2421 return SWITCH_STATUS_GENERR;
2422
2423 if (!switch_channel_test_flag(member->channel, CF_VIDEO)) {
2424 return SWITCH_STATUS_FALSE;
2425 }
2426
2427 conference_member_set_logo(member, text);
2428
2429 layer = conference_video_get_layer_locked(member);
2430
2431 if (!layer) {
2432 goto end;
2433 }
2434
2435 conference_video_layer_set_logo(member, layer);
2436
2437 end:
2438
2439 stream->write_function(stream, "+OK Video logo %s\n", member->video_logo ? "set" : "cleared");
2440
2441 conference_video_release_layer(&layer);
2442
2443 return SWITCH_STATUS_SUCCESS;
2444
2445 }
2446
2447
conference_api_sub_vid_codec_group(conference_member_t * member,switch_stream_handle_t * stream,void * data)2448 switch_status_t conference_api_sub_vid_codec_group(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2449 {
2450 char *text = (char *) data;
2451
2452 if (member == NULL)
2453 return SWITCH_STATUS_GENERR;
2454
2455 if (!switch_channel_test_flag(member->channel, CF_VIDEO)) {
2456 return SWITCH_STATUS_FALSE;
2457 }
2458
2459 if (text) {
2460
2461 if (!strcmp(text, "clear")) {
2462 member->video_codec_group = NULL;
2463 } else {
2464 member->video_codec_group = switch_core_strdup(member->pool, text);
2465 }
2466
2467 switch_mutex_lock(member->conference->member_mutex);
2468 member->video_codec_index = -1;
2469 switch_mutex_unlock(member->conference->member_mutex);
2470 stream->write_function(stream, "+OK Video codec group %s %s\n", member->video_codec_group ? "set" : "cleared", switch_str_nil(member->video_codec_group));
2471 } else {
2472 stream->write_function(stream, "+OK Video codec group is %s\n", member->video_codec_group);
2473 }
2474
2475
2476 return SWITCH_STATUS_SUCCESS;
2477
2478 }
2479
conference_api_sub_get_uuid(conference_member_t * member,switch_stream_handle_t * stream,void * data)2480 switch_status_t conference_api_sub_get_uuid(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2481 {
2482 if (member->session) {
2483 stream->write_function(stream, "%s", switch_core_session_get_uuid(member->session));
2484 } else {
2485 stream->write_function(stream, "_undef_");
2486 }
2487
2488 return SWITCH_STATUS_SUCCESS;
2489 }
2490
clear_res_id(conference_obj_t * conference,conference_member_t * member,const char * id)2491 static void clear_res_id(conference_obj_t *conference, conference_member_t *member, const char *id)
2492 {
2493 conference_member_t *imember;
2494
2495 switch_mutex_lock(conference->member_mutex);
2496 for (imember = conference->members; imember; imember = imember->next) {
2497 if (imember == member) {
2498 continue;
2499 }
2500
2501 if (imember->video_reservation_id && !strcasecmp(imember->video_reservation_id, id)) {
2502 imember->video_reservation_id = NULL;
2503 conference_video_detach_video_layer(imember);
2504 }
2505 }
2506 switch_mutex_unlock(conference->member_mutex);
2507 }
2508
clear_role_id(conference_obj_t * conference,conference_member_t * member,const char * id)2509 static void clear_role_id(conference_obj_t *conference, conference_member_t *member, const char *id)
2510 {
2511 conference_member_t *imember;
2512
2513 switch_mutex_lock(conference->member_mutex);
2514 for (imember = conference->members; imember; imember = imember->next) {
2515 if (imember == member) {
2516 continue;
2517 }
2518
2519 if (imember->video_role_id && !strcasecmp(imember->video_role_id, id)) {
2520 imember->video_role_id = NULL;
2521 conference_video_detach_video_layer(imember);
2522 }
2523 }
2524 switch_mutex_unlock(conference->member_mutex);
2525 }
2526
conference_api_sub_vid_res_id(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2527 switch_status_t conference_api_sub_vid_res_id(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2528 {
2529 uint8_t all = 0, clear = 0, force = 0;
2530 uint32_t member_id;
2531 char *res_id = NULL;
2532 conference_member_t *member;
2533
2534 if (argc < 3 || argc > 5)
2535 return SWITCH_STATUS_GENERR;
2536
2537 res_id = argv[3];
2538
2539 if (argc > 3) {
2540 if (!strcasecmp(res_id, "clear")) {
2541 clear = 1;
2542 }
2543 } else {
2544 clear = 1;
2545 }
2546
2547 if (argc > 4)
2548 force = strcasecmp(argv[4], "force") ? 0 : 1;
2549
2550 if (!(member_id = atoi(argv[2]))) {
2551 all = strcasecmp(argv[2], "all") ? 0 : 1;
2552 }
2553
2554 if (all && clear) {
2555 switch_mutex_lock(conference->member_mutex);
2556 for (member = conference->members; member; member = member->next) {
2557 if (member->session && !conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
2558 conference_api_sub_vid_res_id_member(member, stream, res_id, clear, force);
2559 }
2560 }
2561 switch_mutex_unlock(conference->member_mutex);
2562 } else if (member_id) {
2563 if (!(member = conference_member_get(conference, member_id)))
2564 return SWITCH_STATUS_GENERR;
2565 conference_api_sub_vid_res_id_member(member, stream, res_id, clear, force);
2566 switch_thread_rwlock_unlock(member->rwlock);
2567 } else {
2568 return SWITCH_STATUS_GENERR;
2569 }
2570
2571 return SWITCH_STATUS_SUCCESS;
2572
2573 }
2574
2575
conference_api_sub_vid_res_id_member(conference_member_t * member,switch_stream_handle_t * stream,char * res_id,int clear,int force)2576 switch_status_t conference_api_sub_vid_res_id_member(conference_member_t *member, switch_stream_handle_t *stream, char *res_id, int clear, int force)
2577 {
2578
2579 if (!switch_channel_test_flag(member->channel, CF_VIDEO)) {
2580 return SWITCH_STATUS_FALSE;
2581 }
2582
2583 if (!member->conference->canvases[0]) {
2584 stream->write_function(stream, "-ERR conference is not in mixing mode\n");
2585 return SWITCH_STATUS_SUCCESS;
2586 }
2587
2588 if (clear || (!force && member->video_reservation_id && !strcasecmp(res_id, member->video_reservation_id))) {
2589 member->video_reservation_id = NULL;
2590 stream->write_function(stream, "+OK reservation_id cleared\n");
2591 conference_video_detach_video_layer(member);
2592 } else {
2593 clear_res_id(member->conference, member, res_id);
2594 if (!member->video_reservation_id || strcmp(member->video_reservation_id, res_id)) {
2595 member->video_reservation_id = switch_core_strdup(member->pool, res_id);
2596 }
2597 stream->write_function(stream, "+OK reservation_id %s\n", res_id);
2598 conference_video_detach_video_layer(member);
2599 conference_video_find_floor(member, SWITCH_FALSE);
2600 }
2601
2602 return SWITCH_STATUS_SUCCESS;
2603
2604 }
2605
2606
conference_api_sub_vid_role_id(conference_member_t * member,switch_stream_handle_t * stream,void * data)2607 switch_status_t conference_api_sub_vid_role_id(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2608 {
2609 char *text = (char *) data;
2610 int force = 0;
2611
2612 if (member == NULL)
2613 return SWITCH_STATUS_GENERR;
2614
2615 if (!switch_channel_test_flag(member->channel, CF_VIDEO)) {
2616 return SWITCH_STATUS_FALSE;
2617 }
2618
2619 if (!member->conference->canvases[0]) {
2620 stream->write_function(stream, "-ERR conference is not in mixing mode\n");
2621 return SWITCH_STATUS_SUCCESS;
2622 }
2623
2624 if (!zstr(text) && *text == '=') {
2625 text++;
2626 force = 1;
2627 }
2628
2629 if (zstr(text) || !strcasecmp(text, "clear") || (!force && member->video_role_id && !strcasecmp(text, member->video_role_id))) {
2630 member->video_role_id = NULL;
2631 stream->write_function(stream, "+OK role_id cleared\n");
2632 } else {
2633 clear_role_id(member->conference, member, text);
2634 if (!member->video_role_id || strcmp(member->video_role_id, text)) {
2635 member->video_role_id = switch_core_strdup(member->pool, text);
2636 }
2637 stream->write_function(stream, "+OK role_id %s\n", text);
2638 conference_video_find_floor(member, SWITCH_FALSE);
2639 }
2640
2641
2642
2643 return SWITCH_STATUS_SUCCESS;
2644
2645 }
2646
conference_api_sub_vid_banner(conference_member_t * member,switch_stream_handle_t * stream,void * data)2647 switch_status_t conference_api_sub_vid_banner(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2648 {
2649 mcu_layer_t *layer = NULL;
2650 char *text = (char *) data;
2651
2652 if (member == NULL) {
2653 return SWITCH_STATUS_GENERR;
2654 }
2655
2656 switch_url_decode(text);
2657
2658 if (zstr(text)) goto end;
2659
2660 member->video_banner_text = switch_core_strdup(member->pool, text);
2661
2662 layer = conference_video_get_layer_locked(member);
2663
2664 if (!layer) goto end;
2665
2666 conference_video_layer_set_banner(member, layer, NULL);
2667
2668 end:
2669
2670 stream->write_function(stream, "+OK\n");
2671
2672 conference_video_release_layer(&layer);
2673
2674 return SWITCH_STATUS_SUCCESS;
2675 }
2676
conference_api_sub_vid_floor(conference_member_t * member,switch_stream_handle_t * stream,void * data)2677 switch_status_t conference_api_sub_vid_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2678 {
2679 int force = 0;
2680
2681 if (member == NULL)
2682 return SWITCH_STATUS_GENERR;
2683
2684 if (!switch_channel_test_flag(member->channel, CF_VIDEO) && !member->avatar_png_img) {
2685 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Channel %s does not have video capability!\n", switch_channel_get_name(member->channel));
2686 return SWITCH_STATUS_FALSE;
2687 }
2688
2689 if (conference_utils_member_test_flag(member, MFLAG_DED_VID_LAYER)) {
2690 if (stream != NULL) {
2691 stream->write_function(stream, "-ERR cannot set floor on a member in an active video role\n");
2692 }
2693
2694 return SWITCH_STATUS_SUCCESS;
2695 }
2696
2697 if (data && switch_stristr("force", (char *) data)) {
2698 force = 1;
2699 }
2700
2701 if (member->conference->video_floor_holder == member->id && conference_utils_test_flag(member->conference, CFLAG_VID_FLOOR_LOCK)) {
2702 conference_utils_clear_flag(member->conference, CFLAG_VID_FLOOR_LOCK);
2703
2704 conference_member_set_floor_holder(member->conference, member, 0);
2705 if (stream == NULL) {
2706 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s OK video floor auto\n", member->conference->name);
2707 } else {
2708 stream->write_function(stream, "+OK floor none\n");
2709 }
2710
2711 } else if (force || member->conference->video_floor_holder == 0) {
2712 conference_utils_set_flag(member->conference, CFLAG_VID_FLOOR_LOCK);
2713 conference_video_set_floor_holder(member->conference, member, SWITCH_TRUE);
2714 if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) {
2715 if (stream == NULL) {
2716 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s OK video floor %d %s\n",
2717 member->conference->name, member->id, switch_channel_get_name(member->channel));
2718 } else {
2719 stream->write_function(stream, "+OK floor %u\n", member->id);
2720 }
2721 }
2722 } else {
2723 if (stream == NULL) {
2724 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s floor already held by %d %s\n",
2725 member->conference->name, member->id, switch_channel_get_name(member->channel));
2726 } else {
2727 stream->write_function(stream, "-ERR floor is held by %u\n", member->conference->video_floor_holder);
2728 }
2729 }
2730
2731 return SWITCH_STATUS_SUCCESS;
2732 }
2733
2734
2735
conference_api_sub_file_seek(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2736 switch_status_t conference_api_sub_file_seek(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2737 {
2738 if (argc == 3) {
2739 switch_mutex_lock(conference->mutex);
2740 conference_fnode_seek(conference->fnode, stream, argv[2]);
2741 switch_mutex_unlock(conference->mutex);
2742
2743 return SWITCH_STATUS_SUCCESS;
2744 }
2745
2746 if (argc == 4) {
2747 uint32_t id = atoi(argv[3]);
2748 conference_member_t *member = conference_member_get(conference, id);
2749 if (member == NULL) {
2750 stream->write_function(stream, "-ERR Member: %u not found.\n", id);
2751 return SWITCH_STATUS_GENERR;
2752 }
2753
2754 switch_mutex_lock(member->fnode_mutex);
2755 conference_fnode_seek(member->fnode, stream, argv[2]);
2756 switch_mutex_unlock(member->fnode_mutex);
2757 switch_thread_rwlock_unlock(member->rwlock);
2758 return SWITCH_STATUS_SUCCESS;
2759 }
2760
2761 return SWITCH_STATUS_GENERR;
2762 }
2763
conference_api_set_moh(conference_obj_t * conference,const char * what)2764 switch_status_t conference_api_set_moh(conference_obj_t *conference, const char *what)
2765 {
2766 if (!what) {
2767 return SWITCH_STATUS_FALSE;
2768 }
2769
2770 if (!strcasecmp(what, "toggle")) {
2771 if (conference_utils_test_flag(conference, CFLAG_NO_MOH)) {
2772 conference_utils_clear_flag(conference, CFLAG_NO_MOH);
2773 } else {
2774 conference_utils_set_flag(conference, CFLAG_NO_MOH);
2775 }
2776 } else if (!strcasecmp(what, "on")) {
2777 conference_utils_clear_flag(conference, CFLAG_NO_MOH);
2778 } else if (!strcasecmp(what, "off")) {
2779 conference_utils_set_flag(conference, CFLAG_NO_MOH);
2780 } else if (!strcasecmp(what, "reset")) {
2781 conference->tmp_moh_sound = NULL;
2782 } else {
2783 conference->tmp_moh_sound = switch_core_strdup(conference->pool, what);
2784 }
2785
2786 if (conference_utils_test_flag(conference, CFLAG_NO_MOH) || conference->tmp_moh_sound) {
2787 conference_file_stop(conference, FILE_STOP_ASYNC);
2788 }
2789
2790 return SWITCH_STATUS_SUCCESS;
2791 }
2792
2793
conference_api_sub_moh(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2794 switch_status_t conference_api_sub_moh(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2795 {
2796
2797 if (conference_api_set_moh(conference, argv[2]) == SWITCH_STATUS_SUCCESS) {
2798 if (stream) {
2799 stream->write_function(stream, "+OK moh\n");
2800 }
2801 } else {
2802 if (stream) {
2803 stream->write_function(stream, "-ERR invalid moh param\n");
2804 }
2805 }
2806
2807 return SWITCH_STATUS_SUCCESS;
2808 }
2809
conference_api_sub_play(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2810 switch_status_t conference_api_sub_play(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2811 {
2812 int ret_status = SWITCH_STATUS_GENERR;
2813 switch_event_t *event;
2814 uint8_t async = 0;
2815
2816 switch_assert(conference != NULL);
2817 switch_assert(stream != NULL);
2818
2819 if ((argc == 4 && !strcasecmp(argv[3], "async")) || (argc == 5 && !strcasecmp(argv[4], "async"))) {
2820 argc--;
2821 async++;
2822 }
2823
2824 if (argc == 3) {
2825 if (conference_file_play(conference, argv[2], 0, NULL, async) == SWITCH_STATUS_SUCCESS) {
2826 stream->write_function(stream, "+OK (play) Playing file %s\n", argv[2]);
2827 if (test_eflag(conference, EFLAG_PLAY_FILE) &&
2828 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
2829 conference_event_add_data(conference, event);
2830
2831 if (conference->fnode && conference->fnode->fh.params) {
2832 switch_event_merge(event, conference->fnode->fh.params);
2833 }
2834
2835 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "play-file");
2836 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "File", argv[2]);
2837 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Async", async ? "true" : "false");
2838 switch_event_fire(&event);
2839 }
2840 } else {
2841 stream->write_function(stream, "-ERR (play) File: %s not found.\n", argv[2] ? argv[2] : "(unspecified)");
2842 }
2843 ret_status = SWITCH_STATUS_SUCCESS;
2844 } else if (argc >= 4) {
2845 uint32_t id = atoi(argv[3]);
2846 conference_member_t *member;
2847 switch_bool_t mux = SWITCH_TRUE;
2848
2849 if (argc > 4 && !strcasecmp(argv[4], "nomux")) {
2850 mux = SWITCH_FALSE;
2851 }
2852
2853 if ((member = conference_member_get(conference, id))) {
2854 if (conference_member_play_file(member, argv[2], 0, mux) == SWITCH_STATUS_SUCCESS) {
2855 stream->write_function(stream, "+OK (play) Playing file %s to member %u\n", argv[2], id);
2856 if (test_eflag(conference, EFLAG_PLAY_FILE_MEMBER) &&
2857 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
2858 conference_member_add_event_data(member, event);
2859
2860 if (member->fnode->fh.params) {
2861 switch_event_merge(event, member->fnode->fh.params);
2862 }
2863
2864 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "play-file-member");
2865 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "File", argv[2]);
2866 switch_event_fire(&event);
2867 }
2868 } else {
2869 stream->write_function(stream, "-ERR (play) File: %s not found.\n", argv[2] ? argv[2] : "(unspecified)");
2870 }
2871 switch_thread_rwlock_unlock(member->rwlock);
2872 ret_status = SWITCH_STATUS_SUCCESS;
2873 } else {
2874 stream->write_function(stream, "-ERR Member: %u not found.\n", id);
2875 }
2876 }
2877
2878 return ret_status;
2879 }
2880
conference_api_sub_say(conference_obj_t * conference,switch_stream_handle_t * stream,const char * text)2881 switch_status_t conference_api_sub_say(conference_obj_t *conference, switch_stream_handle_t *stream, const char *text)
2882 {
2883 switch_event_t *event;
2884
2885 if (zstr(text)) {
2886 stream->write_function(stream, "-ERR (say) Error! No text.\n");
2887 return SWITCH_STATUS_GENERR;
2888 }
2889
2890 if (conference_say(conference, text, 0) != SWITCH_STATUS_SUCCESS) {
2891 stream->write_function(stream, "-ERR (say) Error!\n");
2892 return SWITCH_STATUS_GENERR;
2893 }
2894
2895 stream->write_function(stream, "+OK (say) OK\n");
2896 if (test_eflag(conference, EFLAG_SPEAK_TEXT) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
2897 conference_event_add_data(conference, event);
2898 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "speak-text");
2899 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Text", text);
2900 switch_event_fire(&event);
2901 }
2902 return SWITCH_STATUS_SUCCESS;
2903 }
2904
conference_api_sub_saymember(conference_obj_t * conference,switch_stream_handle_t * stream,const char * text)2905 switch_status_t conference_api_sub_saymember(conference_obj_t *conference, switch_stream_handle_t *stream, const char *text)
2906 {
2907 int ret_status = SWITCH_STATUS_GENERR;
2908 char *expanded = NULL;
2909 char *start_text = NULL;
2910 char *workspace = NULL;
2911 uint32_t id = 0;
2912 conference_member_t *member = NULL;
2913 switch_event_t *event;
2914
2915 if (zstr(text)) {
2916 stream->write_function(stream, "-ERR (saymember) No Text!\n");
2917 goto done;
2918 }
2919
2920 if (!(workspace = strdup(text))) {
2921 stream->write_function(stream, "-ERR (saymember) Memory Error!\n");
2922 goto done;
2923 }
2924
2925 if ((start_text = strchr(workspace, ' '))) {
2926 *start_text++ = '\0';
2927 text = start_text;
2928 }
2929
2930 id = atoi(workspace);
2931
2932 if (!id || zstr(text)) {
2933 stream->write_function(stream, "-ERR (saymember) No Text!\n");
2934 goto done;
2935 }
2936
2937 if (!(member = conference_member_get(conference, id))) {
2938 stream->write_function(stream, "-ERR (saymember) Unknown Member %u!\n", id);
2939 goto done;
2940 }
2941
2942 if ((expanded = switch_channel_expand_variables(switch_core_session_get_channel(member->session), (char *) text)) != text) {
2943 text = expanded;
2944 } else {
2945 expanded = NULL;
2946 }
2947
2948 if (!text || conference_member_say(member, (char *) text, 0) != SWITCH_STATUS_SUCCESS) {
2949 stream->write_function(stream, "-ERR (saymember) Error!\n");
2950 goto done;
2951 }
2952
2953 stream->write_function(stream, "-ERR (saymember) OK\n");
2954 if (test_eflag(member->conference, EFLAG_SPEAK_TEXT_MEMBER) &&
2955 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
2956 conference_member_add_event_data(member, event);
2957 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "speak-text-member");
2958 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Text", text);
2959 switch_event_fire(&event);
2960 }
2961 ret_status = SWITCH_STATUS_SUCCESS;
2962
2963 done:
2964
2965 if (member) {
2966 switch_thread_rwlock_unlock(member->rwlock);
2967 }
2968
2969 switch_safe_free(workspace);
2970 switch_safe_free(expanded);
2971 return ret_status;
2972 }
2973
conference_api_sub_cam(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2974 switch_status_t conference_api_sub_cam(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2975 {
2976 int x;
2977 int canvas_id = -1;
2978 int layer_id = -1;
2979 int ok = 0;
2980 mcu_canvas_t *canvas = NULL;
2981 mcu_layer_t *layer = NULL;
2982
2983 if (!conference->canvases[0]) {
2984 stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
2985 return SWITCH_STATUS_SUCCESS;
2986 }
2987
2988 if (argc > 4) {
2989 canvas_id = atoi(argv[2]);
2990 layer_id = atoi(argv[3]);
2991
2992 if (canvas_id > -1 && layer_id > -1 && canvas_id < conference->canvas_count) {
2993 switch_mutex_lock(conference->canvas_mutex);
2994 canvas = conference->canvases[canvas_id];
2995 switch_mutex_lock(canvas->mutex);
2996 if (layer_id < canvas->total_layers) {
2997 layer = &canvas->layers[layer_id];
2998 ok = 1;
2999 for (x = 4; x < argc; x++) {
3000 char *p = strchr(argv[x], '=');
3001 int val = -1, isfalse = 0;
3002 char *str_arg = NULL;
3003
3004 if (p) {
3005 *p++ = '\0';
3006
3007 if (!strcasecmp(argv[x], "zoom") || !strcasecmp(argv[x], "pan")) {
3008 str_arg = p;
3009 if (switch_false(p)) {
3010 isfalse = 1;
3011 }
3012 } else {
3013 if (switch_is_number(p)) {
3014 val = atoi(p);
3015 } else if (switch_true(p)) {
3016 val = 1;
3017 } else {
3018 val = 0;
3019 isfalse = 1;
3020 }
3021 }
3022 } else if (!strcasecmp(argv[x], "reset")) {
3023 str_arg = "true";
3024 }
3025
3026 if (val < 0 && !str_arg) {
3027 stream->write_function(stream, "-ERR invalid val for option [%s]\n", argv[x]);
3028 continue;
3029 }
3030
3031 if (!strcasecmp(argv[x], "autozoom")) {
3032 if ((layer->cam_opts.autozoom = val)) {
3033 layer->cam_opts.manual_zoom = 0;
3034 }
3035
3036 } else if (!strcasecmp(argv[x], "autopan")) {
3037 if ((layer->cam_opts.autopan = val)) {
3038 layer->cam_opts.manual_pan = 0;
3039 }
3040 } else if (!strcasecmp(argv[x], "zoom_factor")) {
3041 if (val > 0 && val < 5) {
3042 layer->cam_opts.zoom_factor = val;
3043 } else {
3044 stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-4\n", argv[x]);
3045 }
3046 } else if (!strcasecmp(argv[x], "snap_factor")) {
3047 if (val > 0 && val < layer->screen_w / 2) {
3048 layer->cam_opts.snap_factor = val;
3049 } else {
3050 stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-%d\n", argv[x], layer->screen_w / 2);
3051 }
3052 } else if (!strcasecmp(argv[x], "zoom_move_factor")) {
3053 if (val > 0 && val < layer->screen_w / 2) {
3054 layer->cam_opts.zoom_move_factor = val;
3055 } else {
3056 stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-4\n", argv[x], layer->screen_w / 2);
3057 }
3058 } else if (!strcasecmp(argv[x], "pan_speed")) {
3059 if (val > 0 && val < 100) {
3060 layer->cam_opts.pan_speed = val;
3061 } else {
3062 stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3063 }
3064 } else if (!strcasecmp(argv[x], "pan_accel_speed")) {
3065 if (val > 0 && val < 100) {
3066 layer->cam_opts.pan_accel_speed = val;
3067 } else {
3068 stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3069 }
3070 } else if (!strcasecmp(argv[x], "pan_accel_min")) {
3071 if (val > 0 && val < 100) {
3072 layer->cam_opts.pan_accel_min = val;
3073 } else {
3074 stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3075 }
3076 } else if (!strcasecmp(argv[x], "zoom_speed")) {
3077 if (val > 0 && val < 100) {
3078 layer->cam_opts.zoom_speed = val;
3079 } else {
3080 stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3081 }
3082 } else if (!strcasecmp(argv[x], "zoom_accel_speed")) {
3083 if (val > 0 && val < 100) {
3084 layer->cam_opts.zoom_accel_speed = val;
3085 } else {
3086 stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3087 }
3088 } else if (!strcasecmp(argv[x], "zoom_accel_min")) {
3089 if (val > 0 && val < 100) {
3090 layer->cam_opts.zoom_accel_min = val;
3091 } else {
3092 stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3093 }
3094 } else if (!strcasecmp(argv[x], "reset")) {
3095 conference_video_reset_layer_cam(layer);
3096 } else if (!strcasecmp(argv[x], "pan")) {
3097 char *x_val = NULL, *y_val = NULL;
3098 int x = -1, y = -1;
3099 int on = 0;
3100
3101 if (isfalse) {
3102 layer->pan_geometry.x = 0;
3103 layer->pan_geometry.y = 0;
3104 layer->cam_opts.manual_pan = 0;
3105 } else {
3106
3107 if (str_arg) {
3108 char *p = strchr(str_arg, ':');
3109
3110 if (p) {
3111 *p++ = '\0';
3112
3113 if (*str_arg == 'x') {
3114 x_val = p;
3115 } else if (*str_arg == 'y') {
3116 y_val = p;
3117 }
3118 }
3119 }
3120
3121 if (!x_val && !y_val) {
3122 stream->write_function(stream, "-ERR invalid val for pan\n");
3123 }
3124
3125 if (x_val) x = atoi(x_val);
3126 if (y_val) y = atoi(y_val);
3127
3128 if (x_val && strrchr(x_val, 'i')) {
3129 int nx = (int)layer->pan_geometry.x + x;
3130
3131 if (nx < 0) nx = 0;
3132 if (nx + layer->pan_geometry.w > layer->img->d_w) nx = layer->img->d_w - layer->pan_geometry.w;
3133
3134 layer->pan_geometry.x = nx;
3135 on++;
3136 } else if (x > -1) {
3137 layer->pan_geometry.x = x;
3138 on++;
3139 }
3140
3141 if (y_val && strrchr(y_val, 'i')) {
3142 int ny = (int)layer->pan_geometry.y + y;
3143
3144 if (ny < 0) ny = 0;
3145 if (ny + layer->pan_geometry.h > layer->img->d_h) ny = layer->img->d_h - layer->pan_geometry.h;
3146
3147 layer->pan_geometry.y = ny;
3148 on++;
3149 } else if (y > -1) {
3150 layer->pan_geometry.y = y;
3151 on++;
3152 }
3153
3154
3155 if (on) {
3156 layer->cam_opts.manual_pan = 1;
3157 layer->cam_opts.autopan = 0;
3158 stream->write_function(stream, "+OK PAN %d,%d\n", layer->pan_geometry.x, layer->pan_geometry.y);
3159 }
3160 }
3161
3162
3163 } else if (!strcasecmp(argv[x], "zoom")) {
3164 if (str_arg && !isfalse) {
3165 char *array[4] = {0};
3166 int iray[4] = {0};
3167 int ac;
3168
3169 if ((ac = switch_split(str_arg, ':', array)) >= 3) {
3170 int i;
3171
3172 for (i = 0; i < ac; i++) {
3173 int tmp = atoi(array[i]);
3174
3175 if (tmp < 0) break;
3176 iray[i] = tmp;
3177 }
3178
3179 if (i == ac) {
3180 layer->cam_opts.manual_zoom = 1;
3181 layer->cam_opts.autozoom = 0;
3182
3183 layer->zoom_geometry.x = iray[0];
3184 layer->zoom_geometry.y = iray[1];
3185 layer->zoom_geometry.w = iray[2];
3186 if (iray[3]) {
3187 layer->zoom_geometry.h = iray[3];
3188 } else {
3189 layer->zoom_geometry.h = iray[2];
3190 }
3191
3192 layer->crop_x = iray[0];
3193 layer->crop_y = iray[1];
3194 layer->crop_w = iray[2];
3195 layer->crop_h = iray[2];
3196 layer->pan_geometry = layer->zoom_geometry;
3197 } else {
3198 ok = 0;
3199 }
3200 }
3201 } else {
3202 layer->zoom_geometry.x = 0;
3203 layer->zoom_geometry.y = 0;
3204 layer->zoom_geometry.w = 0;
3205 layer->zoom_geometry.h = 0;
3206 layer->cam_opts.manual_zoom = 0;
3207 }
3208 } else {
3209 stream->write_function(stream, "-ERR invalid option [%s]\n", argv[x]);
3210 }
3211 }
3212 }
3213 switch_mutex_unlock(canvas->mutex);
3214 switch_mutex_unlock(conference->canvas_mutex);
3215
3216 }
3217 }
3218
3219 if (ok) {
3220 stream->write_function(stream, "+OK\n");
3221 } else {
3222 stream->write_function(stream, "-ERR invalid args\n");
3223 }
3224
3225
3226 return SWITCH_STATUS_SUCCESS;
3227 }
3228
conference_api_sub_stop(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3229 switch_status_t conference_api_sub_stop(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3230 {
3231 uint8_t current = 0, all = 0, async = 0;
3232
3233 switch_assert(conference != NULL);
3234 switch_assert(stream != NULL);
3235
3236 if (argc > 2) {
3237 current = strcasecmp(argv[2], "current") ? 0 : 1;
3238 all = strcasecmp(argv[2], "all") ? 0 : 1;
3239 async = strcasecmp(argv[2], "async") ? 0 : 1;
3240 } else {
3241 all = 1;
3242 }
3243
3244 if (!(current || all || async))
3245 return SWITCH_STATUS_GENERR;
3246
3247 if (argc == 4) {
3248 uint32_t id = atoi(argv[3]);
3249 conference_member_t *member;
3250
3251 if ((member = conference_member_get(conference, id))) {
3252 uint32_t stopped = conference_member_stop_file(member, async ? FILE_STOP_ASYNC : current ? FILE_STOP_CURRENT : FILE_STOP_ALL);
3253 stream->write_function(stream, "+OK Stopped %u files.\n", stopped);
3254 switch_thread_rwlock_unlock(member->rwlock);
3255 } else {
3256 stream->write_function(stream, "-ERR Member: %u not found.\n", id);
3257 }
3258 } else {
3259 uint32_t stopped = conference_file_stop(conference, async ? FILE_STOP_ASYNC : current ? FILE_STOP_CURRENT : FILE_STOP_ALL);
3260 stream->write_function(stream, "+OK Stopped %u files.\n", stopped);
3261 }
3262 return SWITCH_STATUS_SUCCESS;
3263 }
3264
_conference_api_sub_relate_show_member_relationships(conference_obj_t * conference,switch_stream_handle_t * stream,uint32_t member_id)3265 void _conference_api_sub_relate_show_member_relationships(conference_obj_t *conference, switch_stream_handle_t *stream, uint32_t member_id)
3266 {
3267 conference_member_t *member;
3268 for (member = conference->members; member; member = member->next) {
3269 conference_relationship_t *rel;
3270
3271 if (member_id > 0 && member->id != member_id) continue;
3272
3273 for (rel = member->relationships; rel; rel = rel->next) {
3274 stream->write_function(stream, "%d -> %d %s%s%s\n", member->id, rel->id,
3275 (rel->flags & RFLAG_CAN_SPEAK) ? "SPEAK " : "NOSPEAK ",
3276 (rel->flags & RFLAG_CAN_HEAR) ? "HEAR " : "NOHEAR ",
3277 (rel->flags & RFLAG_CAN_SEND_VIDEO) ? "SENDVIDEO " : "NOSENDVIDEO ");
3278 }
3279 }
3280 }
3281
_conference_api_sub_relate_clear_member_relationship(conference_obj_t * conference,switch_stream_handle_t * stream,uint32_t id,uint32_t oid)3282 void _conference_api_sub_relate_clear_member_relationship(conference_obj_t *conference, switch_stream_handle_t *stream, uint32_t id, uint32_t oid)
3283 {
3284 conference_member_t *member = NULL, *other_member = NULL;
3285 if ((member = conference_member_get(conference, id))) {
3286 conference_member_del_relationship(member, oid);
3287 other_member = conference_member_get(conference, oid);
3288
3289 if (other_member) {
3290 if (conference_utils_member_test_flag(other_member, MFLAG_RECEIVING_VIDEO)) {
3291 conference_utils_member_clear_flag(other_member, MFLAG_RECEIVING_VIDEO);
3292 if (conference->floor_holder) {
3293 conference_member_t *omember = NULL;
3294
3295 if ((omember = conference_member_get(member->conference, conference->floor_holder))) {
3296 switch_core_session_request_video_refresh(omember->session);
3297 switch_thread_rwlock_unlock(omember->rwlock);
3298 }
3299 }
3300 }
3301 switch_thread_rwlock_unlock(other_member->rwlock);
3302 }
3303
3304 stream->write_function(stream, "+OK relationship %u->%u cleared.\n", id, oid);
3305 switch_thread_rwlock_unlock(member->rwlock);
3306 } else {
3307 stream->write_function(stream, "-ERR relationship %u->%u not found.\n", id, oid);
3308 }
3309 }
3310
_conference_api_sub_relate_set_member_relationship(conference_obj_t * conference,switch_stream_handle_t * stream,uint32_t id,uint32_t oid,uint8_t nospeak,uint8_t nohear,uint8_t sendvideo,char * action)3311 void _conference_api_sub_relate_set_member_relationship(conference_obj_t *conference, switch_stream_handle_t *stream, uint32_t id, uint32_t oid, uint8_t nospeak, uint8_t nohear, uint8_t sendvideo, char *action)
3312 {
3313
3314 conference_member_t *member = NULL, *other_member = NULL;
3315
3316 if ((member = conference_member_get(conference, id))) {
3317 other_member = conference_member_get(conference, oid);
3318 }
3319
3320 if (member && other_member) {
3321 conference_relationship_t *rel = NULL;
3322
3323 if (sendvideo && conference_utils_member_test_flag(other_member, MFLAG_RECEIVING_VIDEO) && (! (nospeak || nohear))) {
3324 stream->write_function(stream, "-ERR member %d already receiving video", oid);
3325 goto skip;
3326 }
3327
3328 if ((rel = conference_member_get_relationship(member, other_member))) {
3329 rel->flags = 0;
3330 } else {
3331 rel = conference_member_add_relationship(member, oid);
3332 }
3333
3334 if (rel) {
3335 switch_set_flag(rel, RFLAG_CAN_SPEAK | RFLAG_CAN_HEAR);
3336 if (nospeak) {
3337 switch_clear_flag(rel, RFLAG_CAN_SPEAK);
3338 conference_utils_member_clear_flag_locked(member, MFLAG_TALKING);
3339 }
3340 if (nohear) {
3341 switch_clear_flag(rel, RFLAG_CAN_HEAR);
3342 }
3343 if (sendvideo) {
3344 switch_set_flag(rel, RFLAG_CAN_SEND_VIDEO);
3345 conference_utils_member_set_flag(other_member, MFLAG_RECEIVING_VIDEO);
3346 switch_core_session_request_video_refresh(member->session);
3347 }
3348
3349 stream->write_function(stream, "+OK %u->%u %s set\n", id, oid, action);
3350 } else {
3351 stream->write_function(stream, "-ERR error!\n");
3352 }
3353 } else {
3354 stream->write_function(stream, "-ERR relationship %u->%u not found.\n", id, oid);
3355 }
3356
3357 skip:
3358 if (member) {
3359 switch_thread_rwlock_unlock(member->rwlock);
3360 }
3361
3362 if (other_member) {
3363 switch_thread_rwlock_unlock(other_member->rwlock);
3364 }
3365 }
3366
conference_api_sub_relate(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3367 switch_status_t conference_api_sub_relate(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3368 {
3369 uint8_t nospeak = 0, nohear = 0, sendvideo = 0, clear = 0;
3370 int members = 0;
3371 int other_members = 0;
3372 char *members_array[100] = { 0 };
3373 char *other_members_array[100] = { 0 };
3374 char *lbuf_members = NULL, *lbuf_other_members = NULL, *action = NULL;
3375
3376 switch_assert(conference != NULL);
3377 switch_assert(stream != NULL);
3378
3379 if (argc <= 3) {
3380 switch_mutex_lock(conference->mutex);
3381
3382 if (conference->relationship_total) {
3383 if (argc == 3) {
3384 char *lbuf = NULL;
3385 lbuf = strdup(argv[2]);
3386 members = switch_separate_string(lbuf, ',', members_array, (sizeof(members_array) / sizeof(members_array[0])));
3387 if (members) {
3388 int i;
3389 uint32_t member_id;
3390 for (i = 0; i < members && members_array[i]; i++) {
3391 member_id = atoi(members_array[i]);
3392 _conference_api_sub_relate_show_member_relationships(conference, stream, member_id);
3393 }
3394 }
3395 switch_safe_free(lbuf);
3396 }
3397
3398 } else {
3399 stream->write_function(stream, "+OK No relationships\n");
3400 }
3401 switch_mutex_unlock(conference->mutex);
3402 return SWITCH_STATUS_SUCCESS;
3403 }
3404
3405 if (argc <= 4)
3406 return SWITCH_STATUS_GENERR;
3407
3408 nospeak = strstr(argv[4], "nospeak") ? 1 : 0;
3409 nohear = strstr(argv[4], "nohear") ? 1 : 0;
3410 sendvideo = strstr(argv[4], "sendvideo") ? 1 : 0;
3411
3412 if (!strcasecmp(argv[4], "clear")) {
3413 clear = 1;
3414 }
3415
3416 if (!(clear || nospeak || nohear || sendvideo)) {
3417 return SWITCH_STATUS_GENERR;
3418 }
3419
3420 lbuf_members = strdup(argv[2]);
3421 lbuf_other_members = strdup(argv[3]);
3422 action = strdup(argv[4]);
3423 members = switch_separate_string(lbuf_members, ',', members_array, (sizeof(members_array) / sizeof(members_array[0])));
3424 other_members = switch_separate_string(lbuf_other_members, ',', other_members_array, (sizeof(other_members_array) / sizeof(other_members_array[0])));
3425 if (members && other_members) {
3426 int i, i2;
3427 uint32_t member_id, other_member_id;
3428 for (i = 0; i < members && members_array[i]; i++) {
3429 member_id = atoi(members_array[i]);
3430 for (i2 = 0; i2 < other_members && other_members_array[i2]; i2++) {
3431 other_member_id = atoi(other_members_array[i2]);
3432 if (clear) {
3433 _conference_api_sub_relate_clear_member_relationship(conference, stream, member_id, other_member_id);
3434 }
3435 if (nospeak || nohear || sendvideo) {
3436 _conference_api_sub_relate_set_member_relationship(conference, stream, member_id, other_member_id, nospeak, nohear, sendvideo, action);
3437 }
3438 }
3439 }
3440 }
3441 switch_safe_free(lbuf_members);
3442 switch_safe_free(lbuf_other_members);
3443 switch_safe_free(action);
3444
3445 return SWITCH_STATUS_SUCCESS;
3446 }
3447
conference_api_sub_lock(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3448 switch_status_t conference_api_sub_lock(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3449 {
3450 switch_event_t *event;
3451
3452 switch_assert(conference != NULL);
3453 switch_assert(stream != NULL);
3454
3455 if (conference->is_locked_sound) {
3456 conference_file_play(conference, conference->is_locked_sound, CONF_DEFAULT_LEADIN, NULL, 0);
3457 }
3458
3459 conference_utils_set_flag_locked(conference, CFLAG_LOCKED);
3460 stream->write_function(stream, "+OK %s locked\n", argv[0]);
3461 if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3462 conference_event_add_data(conference, event);
3463 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "lock");
3464 switch_event_fire(&event);
3465 }
3466
3467 return 0;
3468 }
3469
conference_api_sub_unlock(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3470 switch_status_t conference_api_sub_unlock(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3471 {
3472 switch_event_t *event;
3473
3474 switch_assert(conference != NULL);
3475 switch_assert(stream != NULL);
3476
3477 if (conference->is_unlocked_sound) {
3478 conference_file_play(conference, conference->is_unlocked_sound, CONF_DEFAULT_LEADIN, NULL, 0);
3479 }
3480
3481 conference_utils_clear_flag_locked(conference, CFLAG_LOCKED);
3482 stream->write_function(stream, "+OK %s unlocked\n", argv[0]);
3483 if (test_eflag(conference, EFLAG_UNLOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3484 conference_event_add_data(conference, event);
3485 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "unlock");
3486 switch_event_fire(&event);
3487 }
3488
3489 return 0;
3490 }
3491
conference_api_sub_exit_sound(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3492 switch_status_t conference_api_sub_exit_sound(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3493 {
3494 switch_event_t *event;
3495
3496 switch_assert(conference != NULL);
3497 switch_assert(stream != NULL);
3498
3499 if (argc <= 2) {
3500 stream->write_function(stream, "-ERR Not enough args\n");
3501 return SWITCH_STATUS_GENERR;
3502 }
3503
3504 if ( !strcasecmp(argv[2], "on") ) {
3505 conference_utils_set_flag_locked(conference, CFLAG_EXIT_SOUND);
3506 stream->write_function(stream, "+OK %s exit sounds on (%s)\n", argv[0], conference->exit_sound);
3507 if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3508 conference_event_add_data(conference, event);
3509 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "exit-sounds-on");
3510 switch_event_fire(&event);
3511 }
3512 } else if ( !strcasecmp(argv[2], "off") || !strcasecmp(argv[2], "none") ) {
3513 conference_utils_clear_flag_locked(conference, CFLAG_EXIT_SOUND);
3514 stream->write_function(stream, "+OK %s exit sounds off (%s)\n", argv[0], conference->exit_sound);
3515 if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3516 conference_event_add_data(conference, event);
3517 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "exit-sounds-off");
3518 switch_event_fire(&event);
3519 }
3520 } else if ( !strcasecmp(argv[2], "file") ) {
3521 if (! argv[3]) {
3522 stream->write_function(stream, "-ERR No filename specified\n");
3523 } else {
3524 /* TODO: if possible, verify file exists before setting it */
3525 stream->write_function(stream,"+OK Old exit sound: [%s]\n", conference->exit_sound);
3526 conference->exit_sound = switch_core_strdup(conference->pool, argv[3]);
3527 stream->write_function(stream, "+OK %s exit sound file set to %s\n", argv[0], conference->exit_sound);
3528 if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3529 conference_event_add_data(conference, event);
3530 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "exit-sound-file-changed");
3531 switch_event_fire(&event);
3532 }
3533 }
3534 } else {
3535 stream->write_function(stream, "-ERR Bad args\n");
3536 return SWITCH_STATUS_GENERR;
3537 }
3538
3539 return 0;
3540 }
3541
3542
conference_api_sub_enter_sound(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3543 switch_status_t conference_api_sub_enter_sound(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3544 {
3545 switch_event_t *event;
3546
3547 switch_assert(conference != NULL);
3548 switch_assert(stream != NULL);
3549
3550 if (argc <= 2) {
3551 stream->write_function(stream, "-ERR Not enough args\n");
3552 return SWITCH_STATUS_GENERR;
3553 }
3554
3555 if ( !strcasecmp(argv[2], "on") ) {
3556 conference_utils_set_flag_locked(conference, CFLAG_ENTER_SOUND);
3557 stream->write_function(stream, "+OK %s enter sounds on (%s)\n", argv[0], conference->enter_sound);
3558 if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3559 conference_event_add_data(conference, event);
3560 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "enter-sounds-on");
3561 switch_event_fire(&event);
3562 }
3563 } else if ( !strcasecmp(argv[2], "off") || !strcasecmp(argv[2], "none") ) {
3564 conference_utils_clear_flag_locked(conference, CFLAG_ENTER_SOUND);
3565 stream->write_function(stream, "+OK %s enter sounds off (%s)\n", argv[0], conference->enter_sound);
3566 if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3567 conference_event_add_data(conference, event);
3568 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "enter-sounds-off");
3569 switch_event_fire(&event);
3570 }
3571 } else if ( !strcasecmp(argv[2], "file") ) {
3572 if (! argv[3]) {
3573 stream->write_function(stream, "-ERR No filename specified\n");
3574 } else {
3575 /* TODO: verify file exists before setting it */
3576 conference->enter_sound = switch_core_strdup(conference->pool, argv[3]);
3577 stream->write_function(stream, "+OK %s enter sound file set to %s\n", argv[0], conference->enter_sound);
3578 if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3579 conference_event_add_data(conference, event);
3580 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "enter-sound-file-changed");
3581 switch_event_fire(&event);
3582 }
3583 }
3584 } else {
3585 stream->write_function(stream, "-ERR Bad args\n");
3586 return SWITCH_STATUS_GENERR;
3587 }
3588
3589 return 0;
3590 }
3591
3592
conference_api_sub_dial(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3593 switch_status_t conference_api_sub_dial(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3594 {
3595 switch_call_cause_t cause;
3596 char *tmp;
3597
3598 switch_assert(stream != NULL);
3599
3600 if (argc <= 2) {
3601 stream->write_function(stream, "-ERR Bad Args\n");
3602 return SWITCH_STATUS_GENERR;
3603 }
3604
3605 if (conference && argv[2] && strstr(argv[2], "vlc/")) {
3606 tmp = switch_core_sprintf(conference->pool, "{vlc_rate=%d,vlc_channels=%d,vlc_interval=%d}%s",
3607 conference->rate, conference->channels, conference->interval, argv[2]);
3608 argv[2] = tmp;
3609 }
3610
3611 if (conference) {
3612 conference_outcall(conference, NULL, NULL, argv[2], 60, NULL, argv[4], argv[3], NULL, &cause, NULL, NULL, NULL);
3613 } else {
3614 conference_outcall(NULL, argv[0], NULL, argv[2], 60, NULL, argv[4], argv[3], NULL, &cause, NULL, NULL, NULL);
3615 }
3616 stream->write_function(stream, "+OK Call Requested: result: [%s]\n", switch_channel_cause2str(cause));
3617
3618 return SWITCH_STATUS_SUCCESS;
3619 }
3620
conference_api_sub_bgdial(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3621 switch_status_t conference_api_sub_bgdial(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3622 {
3623 switch_uuid_t uuid;
3624 char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
3625
3626 switch_assert(stream != NULL);
3627
3628 if (argc <= 2) {
3629 stream->write_function(stream, "-ERR Bad Args\n");
3630 return SWITCH_STATUS_GENERR;
3631 }
3632
3633 switch_uuid_get(&uuid);
3634 switch_uuid_format(uuid_str, &uuid);
3635
3636 if (conference) {
3637 conference_outcall_bg(conference, NULL, NULL, argv[2], 60, NULL, argv[4], argv[3], uuid_str, NULL, NULL, NULL);
3638 } else {
3639 conference_outcall_bg(NULL, argv[0], NULL, argv[2], 60, NULL, argv[4], argv[3], uuid_str, NULL, NULL, NULL);
3640 }
3641
3642 stream->write_function(stream, "+OK Job-UUID: %s\n", uuid_str);
3643
3644 return SWITCH_STATUS_SUCCESS;
3645 }
3646
3647
3648
conference_api_sub_transfer(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3649 switch_status_t conference_api_sub_transfer(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3650 {
3651 switch_status_t ret_status = SWITCH_STATUS_SUCCESS;
3652 char *conference_name = NULL, *profile_name;
3653
3654 switch_assert(conference != NULL);
3655 switch_assert(stream != NULL);
3656
3657 if (argc > 3 && !zstr(argv[2])) {
3658 int x;
3659
3660 conference_name = strdup(argv[2]);
3661 switch_assert(conference_name);
3662
3663 if ((profile_name = strchr(conference_name, '@'))) {
3664 *profile_name++ = '\0';
3665 } else {
3666 profile_name = "default";
3667 }
3668
3669 for (x = 3; x < argc; x++) {
3670 conference_member_t *member = NULL;
3671 uint32_t id = atoi(argv[x]);
3672 switch_channel_t *channel;
3673 switch_event_t *event;
3674 char *xdest = NULL;
3675
3676 if (!id || !(member = conference_member_get(conference, id))) {
3677 stream->write_function(stream, "-ERR No Member %u in conference %s.\n", id, conference->name);
3678 continue;
3679 }
3680
3681 channel = switch_core_session_get_channel(member->session);
3682 xdest = switch_core_session_sprintf(member->session, "conference:%s@%s", conference_name, profile_name);
3683 switch_ivr_session_transfer(member->session, xdest, "inline", NULL);
3684
3685 switch_channel_set_variable(channel, "last_transfered_conference", conference_name);
3686
3687 stream->write_function(stream, "+OK Member '%d' sent to conference %s.\n", member->id, argv[2]);
3688
3689 /* tell them what happened */
3690 if (test_eflag(conference, EFLAG_TRANSFER) &&
3691 switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3692 conference_member_add_event_data(member, event);
3693 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Old-Conference-Name", conference->name);
3694 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "New-Conference-Name", argv[2]);
3695 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "transfer");
3696 switch_event_fire(&event);
3697 }
3698
3699 switch_thread_rwlock_unlock(member->rwlock);
3700 }
3701 } else {
3702 ret_status = SWITCH_STATUS_GENERR;
3703 }
3704
3705 switch_safe_free(conference_name);
3706
3707 return ret_status;
3708 }
3709
conference_api_sub_check_record(conference_obj_t * conference,switch_stream_handle_t * stream,int arc,char ** argv)3710 switch_status_t conference_api_sub_check_record(conference_obj_t *conference, switch_stream_handle_t *stream, int arc, char **argv)
3711 {
3712 conference_record_t *rec;
3713 int x = 0;
3714
3715 switch_mutex_lock(conference->flag_mutex);
3716 for (rec = conference->rec_node_head; rec; rec = rec->next) {
3717 stream->write_function(stream, "+OK Record file %s%s%s\n", rec->path, rec->autorec ? " " : "", rec->autorec ? "(Auto)" : "");
3718 x++;
3719 }
3720
3721 if (!x) {
3722 stream->write_function(stream, "-ERR Conference is not being recorded.\n");
3723 }
3724 switch_mutex_unlock(conference->flag_mutex);
3725
3726 return SWITCH_STATUS_SUCCESS;
3727 }
3728
conference_api_sub_record(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3729 switch_status_t conference_api_sub_record(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3730 {
3731 int id = 0;
3732 conference_record_t *rec;
3733
3734 switch_assert(conference != NULL);
3735 switch_assert(stream != NULL);
3736
3737 if (argc <= 2) {
3738 return SWITCH_STATUS_GENERR;
3739 }
3740
3741 if (argv[3]) {
3742
3743 id = atoi(argv[3]);
3744
3745 if (id < 1 || id > MAX_CANVASES+1) {
3746 id = -1;
3747 }
3748
3749 if (id < 1) {
3750 stream->write_function(stream, "-ERR Invalid canvas\n");
3751 }
3752
3753 }
3754
3755 if (id == 0 && conference->canvases[0]) id = 1;
3756
3757 if (id > conference->canvas_count + 1) {
3758 id = 1;
3759 }
3760
3761
3762 switch_mutex_lock(conference->flag_mutex);
3763 for (rec = conference->rec_node_head; rec; rec = rec->next) {
3764 char *path_a, *path_b;
3765
3766 if ((path_a = strrchr(rec->path, '}'))) {
3767 while(*path_a == ' ' || *path_a == '}') path_a++;
3768 } else {
3769 path_a = rec->path;
3770 }
3771
3772 if ((path_b = strrchr(argv[2], '}'))) {
3773 while(*path_b == ' ' || *path_b == '}') path_b++;
3774 } else {
3775 path_b = argv[2];
3776 }
3777
3778 if (!strcmp(path_a, path_b)) {
3779 stream->write_function(stream, "-ERR file [%s] is already being used for recording.\n", rec->path);
3780 switch_mutex_unlock(conference->flag_mutex);
3781 return SWITCH_STATUS_SUCCESS;
3782 }
3783 }
3784 switch_mutex_unlock(conference->flag_mutex);
3785
3786
3787 if (id > 0) {
3788 stream->write_function(stream, "+OK Record file %s canvas %d\n", argv[2], id);
3789 } else {
3790 stream->write_function(stream, "+OK Record file %s\n", argv[2]);
3791 }
3792
3793 conference->record_filename = switch_core_strdup(conference->pool, argv[2]);
3794 conference->record_count++;
3795 conference_record_launch_thread(conference, argv[2], id - 1, SWITCH_FALSE);
3796 return SWITCH_STATUS_SUCCESS;
3797 }
3798
conference_api_sub_norecord(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3799 switch_status_t conference_api_sub_norecord(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3800 {
3801 int all, before = conference->record_count, ttl = 0;
3802
3803 switch_assert(conference != NULL);
3804 switch_assert(stream != NULL);
3805
3806 if (argc <= 2)
3807 return SWITCH_STATUS_GENERR;
3808
3809 all = (strcasecmp(argv[2], "all") == 0);
3810
3811 if (!conference_record_stop(conference, stream, all ? NULL : argv[2]) && !all) {
3812 stream->write_function(stream, "-ERR non-existant recording '%s'\n", argv[2]);
3813 }
3814
3815 ttl = before - conference->record_count;
3816 stream->write_function(stream, "+OK Stopped recording %d file%s\n", ttl, ttl == 1 ? "" : "s");
3817
3818 return SWITCH_STATUS_SUCCESS;
3819 }
3820
conference_api_sub_pauserec(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3821 switch_status_t conference_api_sub_pauserec(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3822 {
3823 switch_event_t *event;
3824 recording_action_type_t action;
3825
3826 switch_assert(conference != NULL);
3827 switch_assert(stream != NULL);
3828
3829 if (argc <= 2)
3830 return SWITCH_STATUS_GENERR;
3831
3832 if (strcasecmp(argv[1], "pause") == 0) {
3833 action = REC_ACTION_PAUSE;
3834 } else if (strcasecmp(argv[1], "resume") == 0) {
3835 action = REC_ACTION_RESUME;
3836 } else {
3837 return SWITCH_STATUS_GENERR;
3838 }
3839 stream->write_function(stream, "+OK %s recording file %s\n",
3840 action == REC_ACTION_PAUSE ? "Pause" : "Resume", argv[2]);
3841 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s recording file %s\n",
3842 action == REC_ACTION_PAUSE ? "Pause" : "Resume", argv[2]);
3843
3844 if (!conference_record_action(conference, argv[2], action)) {
3845 stream->write_function(stream, "-ERR non-existant recording '%s'\n", argv[2]);
3846 } else {
3847 if (test_eflag(conference, EFLAG_RECORD) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS)
3848 {
3849 conference_event_add_data(conference, event);
3850 if (action == REC_ACTION_PAUSE) {
3851 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "pause-recording");
3852 } else {
3853 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "resume-recording");
3854 }
3855 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Path", argv[2]);
3856 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Other-Recordings", conference->record_count ? "true" : "false");
3857 switch_event_fire(&event);
3858 }
3859 }
3860
3861 return SWITCH_STATUS_SUCCESS;
3862 }
3863
conference_api_sub_recording(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3864 switch_status_t conference_api_sub_recording(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3865 {
3866 switch_assert(conference != NULL);
3867 switch_assert(stream != NULL);
3868
3869 if (argc > 2 && argc <= 3) {
3870 if (strcasecmp(argv[2], "stop") == 0 || strcasecmp(argv[2], "check") == 0) {
3871 argv[3] = "all";
3872 argc++;
3873 }
3874 }
3875
3876 if (argc <= 3) {
3877 /* It means that old syntax is used */
3878 return conference_api_sub_record(conference,stream,argc,argv);
3879 } else {
3880 /* for new syntax call existing functions with fixed parameter list */
3881 if (strcasecmp(argv[2], "start") == 0) {
3882 argv[1] = argv[2];
3883 argv[2] = argv[3];
3884 argv[3] = argv[4];
3885 return conference_api_sub_record(conference,stream,4,argv);
3886 } else if (strcasecmp(argv[2], "stop") == 0) {
3887 argv[1] = argv[2];
3888 argv[2] = argv[3];
3889 return conference_api_sub_norecord(conference,stream,4,argv);
3890 } else if (strcasecmp(argv[2], "check") == 0) {
3891 argv[1] = argv[2];
3892 argv[2] = argv[3];
3893 return conference_api_sub_check_record(conference,stream,4,argv);
3894 } else if (strcasecmp(argv[2], "pause") == 0) {
3895 argv[1] = argv[2];
3896 argv[2] = argv[3];
3897 return conference_api_sub_pauserec(conference,stream,4,argv);
3898 } else if (strcasecmp(argv[2], "resume") == 0) {
3899 argv[1] = argv[2];
3900 argv[2] = argv[3];
3901 return conference_api_sub_pauserec(conference,stream,4,argv);
3902 } else {
3903 return SWITCH_STATUS_GENERR;
3904 }
3905 }
3906 }
3907
conference_api_sub_file_vol(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3908 switch_status_t conference_api_sub_file_vol(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3909 {
3910 if (argc >= 1) {
3911 conference_file_node_t *fnode;
3912 int vol = 0;
3913 int ok = 0;
3914
3915 if (argc < 3) {
3916 stream->write_function(stream, "-ERR missing args\n");
3917 return SWITCH_STATUS_GENERR;
3918 }
3919
3920 switch_mutex_lock(conference->mutex);
3921
3922 fnode = conference->fnode;
3923
3924 vol = atoi(argv[2]);
3925
3926 if (argc > 3) {
3927 if (strcasecmp(argv[3], "async")) {
3928 fnode = conference->async_fnode;
3929 }
3930 }
3931
3932 if (fnode && fnode->type == NODE_TYPE_FILE) {
3933 fnode->fh.vol = vol;
3934 ok = 1;
3935 }
3936 switch_mutex_unlock(conference->mutex);
3937
3938
3939 if (ok) {
3940 stream->write_function(stream, "+OK volume changed\n");
3941 return SWITCH_STATUS_SUCCESS;
3942 } else {
3943 stream->write_function(stream, "-ERR File not playing\n");
3944 return SWITCH_STATUS_GENERR;
3945 }
3946
3947
3948 } else {
3949 stream->write_function(stream, "-ERR Invalid parameters:\n");
3950 return SWITCH_STATUS_GENERR;
3951 }
3952 }
3953
conference_api_sub_pin(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3954 switch_status_t conference_api_sub_pin(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3955 {
3956 switch_assert(conference != NULL);
3957 switch_assert(stream != NULL);
3958
3959 if ((argc == 4) && (!strcmp(argv[2], "mod"))) {
3960 conference->mpin = switch_core_strdup(conference->pool, argv[3]);
3961 stream->write_function(stream, "+OK Moderator Pin for conference %s set: %s\n", argv[0], conference->mpin);
3962 return SWITCH_STATUS_SUCCESS;
3963 } else if ((argc == 3) && (!strcmp(argv[1], "pin"))) {
3964 conference->pin = switch_core_strdup(conference->pool, argv[2]);
3965 stream->write_function(stream, "+OK Pin for conference %s set: %s\n", argv[0], conference->pin);
3966 return SWITCH_STATUS_SUCCESS;
3967 } else if (argc == 2 && (!strcmp(argv[1], "nopin"))) {
3968 conference->pin = NULL;
3969 stream->write_function(stream, "+OK Pin for conference %s deleted\n", argv[0]);
3970 return SWITCH_STATUS_SUCCESS;
3971 } else {
3972 stream->write_function(stream, "-ERR Invalid parameters:\n");
3973 return SWITCH_STATUS_GENERR;
3974 }
3975 }
3976
conference_api_sub_get(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3977 switch_status_t conference_api_sub_get(conference_obj_t *conference,
3978 switch_stream_handle_t *stream, int argc, char **argv) {
3979 int ret_status = SWITCH_STATUS_GENERR;
3980
3981 if (argc != 3) {
3982 ret_status = SWITCH_STATUS_FALSE;
3983 } else {
3984 ret_status = SWITCH_STATUS_SUCCESS;
3985 if (strcasecmp(argv[2], "run_time") == 0) {
3986 stream->write_function(stream, "%ld",
3987 switch_epoch_time_now(NULL) - conference->run_time);
3988 } else if (strcasecmp(argv[2], "count") == 0) {
3989 stream->write_function(stream, "%d",
3990 conference->count);
3991 } else if (strcasecmp(argv[2], "count_ghosts") == 0) {
3992 stream->write_function(stream, "%d",
3993 conference->count_ghosts);
3994 } else if (strcasecmp(argv[2], "max_members") == 0) {
3995 stream->write_function(stream, "%d",
3996 conference->max_members);
3997 } else if (strcasecmp(argv[2], "rate") == 0) {
3998 stream->write_function(stream, "%d",
3999 conference->rate);
4000 } else if (strcasecmp(argv[2], "profile_name") == 0) {
4001 stream->write_function(stream, "%s",
4002 conference->profile_name);
4003 } else if (strcasecmp(argv[2], "sound_prefix") == 0) {
4004 stream->write_function(stream, "%s",
4005 conference->sound_prefix);
4006 } else if (strcasecmp(argv[2], "caller_id_name") == 0) {
4007 stream->write_function(stream, "%s",
4008 conference->caller_id_name);
4009 } else if (strcasecmp(argv[2], "caller_id_number") == 0) {
4010 stream->write_function(stream, "%s",
4011 conference->caller_id_number);
4012 } else if (strcasecmp(argv[2], "is_locked") == 0) {
4013 stream->write_function(stream, "%s",
4014 conference_utils_test_flag(conference, CFLAG_LOCKED) ? "locked" : "");
4015 } else if (strcasecmp(argv[2], "endconference_grace_time") == 0) {
4016 stream->write_function(stream, "%d",
4017 conference->endconference_grace_time);
4018 } else if (strcasecmp(argv[2], "uuid") == 0) {
4019 stream->write_function(stream, "%s",
4020 conference->uuid_str);
4021 } else if (strcasecmp(argv[2], "wait_mod") == 0) {
4022 stream->write_function(stream, "%s",
4023 conference_utils_test_flag(conference, CFLAG_WAIT_MOD) ? "true" : "");
4024 } else {
4025 ret_status = SWITCH_STATUS_FALSE;
4026 }
4027 }
4028
4029 return ret_status;
4030 }
4031
conference_api_sub_set(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)4032 switch_status_t conference_api_sub_set(conference_obj_t *conference,
4033 switch_stream_handle_t *stream, int argc, char **argv) {
4034 int ret_status = SWITCH_STATUS_GENERR;
4035
4036 if (argc != 4 || zstr(argv[3])) {
4037 ret_status = SWITCH_STATUS_FALSE;
4038 } else {
4039 ret_status = SWITCH_STATUS_SUCCESS;
4040 if (strcasecmp(argv[2], "max_members") == 0) {
4041 int new_max = atoi(argv[3]);
4042 if (new_max >= 0) {
4043 stream->write_function(stream, "%d", conference->max_members);
4044 conference->max_members = new_max;
4045 } else {
4046 ret_status = SWITCH_STATUS_FALSE;
4047 }
4048 } else if (strcasecmp(argv[2], "sound_prefix") == 0) {
4049 stream->write_function(stream, "%s",conference->sound_prefix);
4050 conference->sound_prefix = switch_core_strdup(conference->pool, argv[3]);
4051 } else if (strcasecmp(argv[2], "caller_id_name") == 0) {
4052 stream->write_function(stream, "%s",conference->caller_id_name);
4053 conference->caller_id_name = switch_core_strdup(conference->pool, argv[3]);
4054 } else if (strcasecmp(argv[2], "caller_id_number") == 0) {
4055 stream->write_function(stream, "%s",conference->caller_id_number);
4056 conference->caller_id_number = switch_core_strdup(conference->pool, argv[3]);
4057 } else if (strcasecmp(argv[2], "endconference_grace_time") == 0) {
4058 int new_gt = atoi(argv[3]);
4059 if (new_gt >= 0) {
4060 stream->write_function(stream, "%d", conference->endconference_grace_time);
4061 conference->endconference_grace_time = new_gt;
4062 } else {
4063 ret_status = SWITCH_STATUS_FALSE;
4064 }
4065 } else {
4066 ret_status = SWITCH_STATUS_FALSE;
4067 }
4068 }
4069
4070 return ret_status;
4071 }
4072
4073
4074
conference_api_sub_xml_list(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)4075 switch_status_t conference_api_sub_xml_list(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
4076 {
4077 int count = 0;
4078 switch_hash_index_t *hi;
4079 void *val;
4080 switch_xml_t x_conference, x_conferences;
4081 int off = 0;
4082 char *ebuf;
4083
4084 x_conferences = switch_xml_new("conferences");
4085 switch_assert(x_conferences);
4086
4087 if (conference == NULL) {
4088 switch_mutex_lock(conference_globals.hash_mutex);
4089 for (hi = switch_core_hash_first(conference_globals.conference_hash); hi; hi = switch_core_hash_next(&hi)) {
4090 switch_core_hash_this(hi, NULL, NULL, &val);
4091 conference = (conference_obj_t *) val;
4092
4093 x_conference = switch_xml_add_child_d(x_conferences, "conference", off++);
4094 switch_assert(conference);
4095
4096 count++;
4097 conference_xlist(conference, x_conference, off);
4098
4099 }
4100 switch_mutex_unlock(conference_globals.hash_mutex);
4101 } else {
4102 x_conference = switch_xml_add_child_d(x_conferences, "conference", off++);
4103 switch_assert(conference);
4104 count++;
4105 conference_xlist(conference, x_conference, off);
4106 }
4107
4108
4109 ebuf = switch_xml_toxml(x_conferences, SWITCH_TRUE);
4110
4111 stream->write_function(stream, "%s", ebuf);
4112
4113 switch_xml_free(x_conferences);
4114 free(ebuf);
4115
4116 return SWITCH_STATUS_SUCCESS;
4117 }
4118
conference_api_sub_json_list(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)4119 switch_status_t conference_api_sub_json_list(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
4120 {
4121 switch_hash_index_t *hi;
4122 void *val;
4123 char *ebuf;
4124 cJSON *conferences = cJSON_CreateArray();
4125 switch_bool_t compact = SWITCH_FALSE;
4126
4127 switch_assert(conferences);
4128
4129 if (conference == NULL) {
4130 switch_mutex_lock(conference_globals.hash_mutex);
4131 for (hi = switch_core_hash_first(conference_globals.conference_hash); hi; hi = switch_core_hash_next(&hi)) {
4132 switch_core_hash_this(hi, NULL, NULL, &val);
4133 conference = (conference_obj_t *) val;
4134 conference_jlist(conference, conferences);
4135 }
4136 switch_mutex_unlock(conference_globals.hash_mutex);
4137 compact = (argc == 2 && !strcmp(argv[1], "compact"));
4138 } else {
4139 conference_jlist(conference, conferences);
4140 compact = (argc == 3 && !strcmp(argv[2], "compact"));
4141 }
4142
4143 ebuf = compact ? cJSON_PrintUnformatted(conferences) : cJSON_Print(conferences);
4144 switch_assert(ebuf);
4145 stream->write_function(stream, "%s", ebuf);
4146 free(ebuf);
4147
4148 cJSON_Delete(conferences);
4149
4150 return SWITCH_STATUS_SUCCESS;
4151 }
4152
conference_api_dispatch(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv,const char * cmdline,int argn)4153 switch_status_t conference_api_dispatch(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv, const char *cmdline, int argn)
4154 {
4155 switch_status_t status = SWITCH_STATUS_FALSE;
4156 uint32_t i, found = 0;
4157 switch_assert(conference != NULL);
4158 switch_assert(stream != NULL);
4159
4160 /* loop through the command table to find a match */
4161 for (i = 0; i < CONFFUNCAPISIZE && !found; i++) {
4162 if (strcasecmp(argv[argn], conference_api_sub_commands[i].pname) == 0) {
4163 found = 1;
4164 switch (conference_api_sub_commands[i].fntype) {
4165
4166 /* commands that we've broken the command line into arguments for */
4167 case CONF_API_SUB_ARGS_SPLIT:
4168 {
4169 conference_api_args_cmd_t pfn = (conference_api_args_cmd_t) conference_api_sub_commands[i].pfnapicmd;
4170
4171 if (pfn(conference, stream, argc, argv) != SWITCH_STATUS_SUCCESS) {
4172 /* command returned error, so show syntax usage */
4173 stream->write_function(stream, "%s %s", conference_api_sub_commands[i].pcommand, conference_api_sub_commands[i].psyntax);
4174 }
4175 }
4176 break;
4177
4178 /* member specific command that can be iterated */
4179 case CONF_API_SUB_MEMBER_TARGET:
4180 {
4181 uint32_t id = 0;
4182 uint8_t all = 0;
4183 uint8_t last = 0;
4184 uint8_t non_mod = 0;
4185
4186 if (argv[argn + 1]) {
4187 if (!(id = atoi(argv[argn + 1]))) {
4188 all = strcasecmp(argv[argn + 1], "all") ? 0 : 1;
4189 non_mod = strcasecmp(argv[argn + 1], "non_moderator") ? 0 : 1;
4190 last = strcasecmp(argv[argn + 1], "last") ? 0 : 1;
4191 }
4192 }
4193
4194 if (all || non_mod) {
4195 conference_member_itterator(conference, stream, non_mod, (conference_api_member_cmd_t) conference_api_sub_commands[i].pfnapicmd, argv[argn + 2]);
4196 } else if (last) {
4197 conference_member_t *member = NULL;
4198 conference_member_t *last_member = NULL;
4199
4200 switch_mutex_lock(conference->member_mutex);
4201
4202 /* find last (oldest) member */
4203 member = conference->members;
4204 while (member != NULL) {
4205 if (last_member == NULL || member->id > last_member->id) {
4206 last_member = member;
4207 }
4208 member = member->next;
4209 }
4210
4211 /* exec functio on last (oldest) member */
4212 if (last_member != NULL && last_member->session && !conference_utils_member_test_flag(last_member, MFLAG_NOCHANNEL)) {
4213 conference_api_member_cmd_t pfn = (conference_api_member_cmd_t) conference_api_sub_commands[i].pfnapicmd;
4214 pfn(last_member, stream, argv[argn + 2]);
4215 }
4216
4217 switch_mutex_unlock(conference->member_mutex);
4218 } else if (id) {
4219 conference_api_member_cmd_t pfn = (conference_api_member_cmd_t) conference_api_sub_commands[i].pfnapicmd;
4220 conference_member_t *member = conference_member_get(conference, id);
4221
4222 if (member != NULL) {
4223 pfn(member, stream, argv[argn + 2]);
4224 switch_thread_rwlock_unlock(member->rwlock);
4225 } else {
4226 stream->write_function(stream, "-ERR Non-Existant ID %u\n", id);
4227 }
4228 } else if (!zstr(argv[argn + 1]) && strchr(argv[argn + 1], '=')) {
4229 conference_api_member_cmd_t pfn = (conference_api_member_cmd_t) conference_api_sub_commands[i].pfnapicmd;
4230 conference_member_t *member;
4231 char *var, *val;
4232
4233 var = strdup(argv[argn + 1]);
4234 switch_assert(var);
4235
4236 if ((val = strchr(var, '='))) {
4237 *val++ = '\0';
4238 }
4239
4240 member = conference_member_get_by_var(conference, var, val);
4241
4242 if (member != NULL) {
4243 pfn(member, stream, argv[argn + 2]);
4244 switch_thread_rwlock_unlock(member->rwlock);
4245 } else {
4246 stream->write_function(stream, "-ERR Non-Existant member\n");
4247 }
4248
4249 switch_safe_free(var);
4250 } else {
4251 stream->write_function(stream, "%s %s", conference_api_sub_commands[i].pcommand, conference_api_sub_commands[i].psyntax);
4252 }
4253 }
4254 break;
4255
4256 /* commands that deals with all text after command */
4257 case CONF_API_SUB_ARGS_AS_ONE:
4258 {
4259 conference_api_text_cmd_t pfn = (conference_api_text_cmd_t) conference_api_sub_commands[i].pfnapicmd;
4260 char *start_text;
4261 const char *modified_cmdline = cmdline;
4262 const char *cmd = conference_api_sub_commands[i].pname;
4263
4264 if (!zstr(modified_cmdline) && (start_text = strstr(modified_cmdline, cmd))) {
4265 modified_cmdline = start_text + strlen(cmd);
4266 while (modified_cmdline && (*modified_cmdline == ' ' || *modified_cmdline == '\t')) {
4267 modified_cmdline++;
4268 }
4269 }
4270
4271 /* call the command handler */
4272 if (pfn(conference, stream, modified_cmdline) != SWITCH_STATUS_SUCCESS) {
4273 /* command returned error, so show syntax usage */
4274 stream->write_function(stream, "%s %s", conference_api_sub_commands[i].pcommand, conference_api_sub_commands[i].psyntax);
4275 }
4276 }
4277 break;
4278 }
4279 }
4280 }
4281
4282 if (!found) {
4283 stream->write_function(stream, "-ERR Conference command '%s' not found.\n", argv[argn]);
4284 } else {
4285 status = SWITCH_STATUS_SUCCESS;
4286 }
4287
4288 return status;
4289 }
4290
4291 /* For Emacs:
4292 * Local Variables:
4293 * mode:c
4294 * indent-tabs-mode:t
4295 * tab-width:4
4296 * c-basic-offset:4
4297 * End:
4298 * For VIM:
4299 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
4300 */
4301