bridge_builtin_features.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2009, Digium, Inc.
  5. *
  6. * Joshua Colp <jcolp@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*! \file
  19. *
  20. * \brief Built in bridging features
  21. *
  22. * \author Joshua Colp <jcolp@digium.com>
  23. *
  24. * \ingroup bridges
  25. */
  26. /*** MODULEINFO
  27. <use type="module">res_monitor</use>
  28. <support_level>core</support_level>
  29. ***/
  30. #include "asterisk.h"
  31. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <sys/types.h>
  36. #include <sys/stat.h>
  37. #include "asterisk/module.h"
  38. #include "asterisk/channel.h"
  39. #include "asterisk/bridge.h"
  40. #include "asterisk/bridge_technology.h"
  41. #include "asterisk/frame.h"
  42. #include "asterisk/file.h"
  43. #include "asterisk/app.h"
  44. #include "asterisk/astobj2.h"
  45. #include "asterisk/pbx.h"
  46. #include "asterisk/parking.h"
  47. #include "asterisk/features_config.h"
  48. #include "asterisk/monitor.h"
  49. #include "asterisk/mixmonitor.h"
  50. #include "asterisk/audiohook.h"
  51. #include "asterisk/causes.h"
  52. enum set_touch_variables_res {
  53. SET_TOUCH_SUCCESS,
  54. SET_TOUCH_UNSET,
  55. SET_TOUCH_ALLOC_FAILURE,
  56. };
  57. static void set_touch_variable(enum set_touch_variables_res *res, struct ast_channel *chan, const char *var_name, char **touch)
  58. {
  59. const char *c_touch;
  60. if (*res == SET_TOUCH_ALLOC_FAILURE) {
  61. return;
  62. }
  63. c_touch = pbx_builtin_getvar_helper(chan, var_name);
  64. if (!ast_strlen_zero(c_touch)) {
  65. *touch = ast_strdup(c_touch);
  66. if (!*touch) {
  67. *res = SET_TOUCH_ALLOC_FAILURE;
  68. } else {
  69. *res = SET_TOUCH_SUCCESS;
  70. }
  71. }
  72. }
  73. static enum set_touch_variables_res set_touch_variables(struct ast_channel *chan, int is_mixmonitor, char **touch_format, char **touch_monitor, char **touch_monitor_prefix)
  74. {
  75. enum set_touch_variables_res res = SET_TOUCH_UNSET;
  76. const char *var_format;
  77. const char *var_monitor;
  78. const char *var_prefix;
  79. SCOPED_CHANNELLOCK(lock, chan);
  80. if (is_mixmonitor) {
  81. var_format = "TOUCH_MIXMONITOR_FORMAT";
  82. var_monitor = "TOUCH_MIXMONITOR";
  83. var_prefix = "TOUCH_MIXMONITOR_PREFIX";
  84. } else {
  85. var_format = "TOUCH_MONITOR_FORMAT";
  86. var_monitor = "TOUCH_MONITOR";
  87. var_prefix = "TOUCH_MONITOR_PREFIX";
  88. }
  89. set_touch_variable(&res, chan, var_format, touch_format);
  90. set_touch_variable(&res, chan, var_monitor, touch_monitor);
  91. set_touch_variable(&res, chan, var_prefix, touch_monitor_prefix);
  92. return res;
  93. }
  94. static void stop_automonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *stop_message)
  95. {
  96. ast_verb(4, "AutoMonitor used to stop recording call.\n");
  97. ast_channel_lock(peer_chan);
  98. if (ast_channel_monitor(peer_chan)) {
  99. if (ast_channel_monitor(peer_chan)->stop(peer_chan, 1)) {
  100. ast_verb(4, "Cannot stop AutoMonitor for %s\n", ast_channel_name(bridge_channel->chan));
  101. if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
  102. ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
  103. }
  104. ast_channel_unlock(peer_chan);
  105. return;
  106. }
  107. } else {
  108. /* Something else removed the Monitor before we got to it. */
  109. ast_channel_unlock(peer_chan);
  110. return;
  111. }
  112. ast_channel_unlock(peer_chan);
  113. if (features_cfg && !(ast_strlen_zero(features_cfg->courtesytone))) {
  114. ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
  115. ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
  116. }
  117. if (!ast_strlen_zero(stop_message)) {
  118. ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
  119. ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL);
  120. }
  121. }
  122. static void start_automonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *start_message)
  123. {
  124. char *touch_filename;
  125. size_t len;
  126. int x;
  127. enum set_touch_variables_res set_touch_res;
  128. RAII_VAR(char *, touch_format, NULL, ast_free);
  129. RAII_VAR(char *, touch_monitor, NULL, ast_free);
  130. RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
  131. set_touch_res = set_touch_variables(bridge_channel->chan, 0, &touch_format,
  132. &touch_monitor, &touch_monitor_prefix);
  133. switch (set_touch_res) {
  134. case SET_TOUCH_SUCCESS:
  135. break;
  136. case SET_TOUCH_UNSET:
  137. set_touch_res = set_touch_variables(peer_chan, 0, &touch_format, &touch_monitor,
  138. &touch_monitor_prefix);
  139. if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
  140. return;
  141. }
  142. break;
  143. case SET_TOUCH_ALLOC_FAILURE:
  144. return;
  145. }
  146. if (!ast_strlen_zero(touch_monitor)) {
  147. len = strlen(touch_monitor) + 50;
  148. touch_filename = ast_alloca(len);
  149. snprintf(touch_filename, len, "%s-%ld-%s",
  150. S_OR(touch_monitor_prefix, "auto"),
  151. (long) time(NULL),
  152. touch_monitor);
  153. } else {
  154. char *caller_chan_id;
  155. char *peer_chan_id;
  156. caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid,
  157. ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan)));
  158. peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid,
  159. ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan)));
  160. len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50;
  161. touch_filename = ast_alloca(len);
  162. snprintf(touch_filename, len, "%s-%ld-%s-%s",
  163. S_OR(touch_monitor_prefix, "auto"),
  164. (long) time(NULL),
  165. caller_chan_id,
  166. peer_chan_id);
  167. }
  168. for (x = 0; x < strlen(touch_filename); x++) {
  169. if (touch_filename[x] == '/') {
  170. touch_filename[x] = '-';
  171. }
  172. }
  173. ast_verb(4, "AutoMonitor used to record call. Filename: %s\n", touch_filename);
  174. if (ast_monitor_start(peer_chan, touch_format, touch_filename, 1, X_REC_IN | X_REC_OUT, NULL)) {
  175. ast_verb(4, "AutoMonitor feature was tried by '%s' but monitor failed to start.\n",
  176. ast_channel_name(bridge_channel->chan));
  177. return;
  178. }
  179. ast_monitor_setjoinfiles(peer_chan, 1);
  180. if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
  181. ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
  182. ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
  183. }
  184. if (!ast_strlen_zero(start_message)) {
  185. ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
  186. ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL);
  187. }
  188. pbx_builtin_setvar_helper(bridge_channel->chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
  189. pbx_builtin_setvar_helper(peer_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
  190. }
  191. static int feature_automonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
  192. {
  193. const char *start_message;
  194. const char *stop_message;
  195. struct ast_bridge_features_automonitor *options = hook_pvt;
  196. enum ast_bridge_features_monitor start_stop = options ? options->start_stop : AUTO_MONITOR_TOGGLE;
  197. int is_monitoring;
  198. RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup);
  199. RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup);
  200. ast_channel_lock(bridge_channel->chan);
  201. features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
  202. ast_channel_unlock(bridge_channel->chan);
  203. ast_bridge_channel_lock_bridge(bridge_channel);
  204. peer_chan = ast_bridge_peer_nolock(bridge_channel->bridge, bridge_channel->chan);
  205. ast_bridge_unlock(bridge_channel->bridge);
  206. if (!peer_chan) {
  207. ast_verb(4, "Cannot start AutoMonitor for %s - can not determine peer in bridge.\n",
  208. ast_channel_name(bridge_channel->chan));
  209. if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
  210. ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
  211. }
  212. return 0;
  213. }
  214. ast_channel_lock(bridge_channel->chan);
  215. start_message = pbx_builtin_getvar_helper(bridge_channel->chan,
  216. "TOUCH_MONITOR_MESSAGE_START");
  217. start_message = ast_strdupa(S_OR(start_message, ""));
  218. stop_message = pbx_builtin_getvar_helper(bridge_channel->chan,
  219. "TOUCH_MONITOR_MESSAGE_STOP");
  220. stop_message = ast_strdupa(S_OR(stop_message, ""));
  221. ast_channel_unlock(bridge_channel->chan);
  222. is_monitoring = ast_channel_monitor(peer_chan) != NULL;
  223. switch (start_stop) {
  224. case AUTO_MONITOR_TOGGLE:
  225. if (is_monitoring) {
  226. stop_automonitor(bridge_channel, peer_chan, features_cfg, stop_message);
  227. } else {
  228. start_automonitor(bridge_channel, peer_chan, features_cfg, start_message);
  229. }
  230. return 0;
  231. case AUTO_MONITOR_START:
  232. if (!is_monitoring) {
  233. start_automonitor(bridge_channel, peer_chan, features_cfg, start_message);
  234. return 0;
  235. }
  236. ast_verb(4, "AutoMonitor already recording call.\n");
  237. break;
  238. case AUTO_MONITOR_STOP:
  239. if (is_monitoring) {
  240. stop_automonitor(bridge_channel, peer_chan, features_cfg, stop_message);
  241. return 0;
  242. }
  243. ast_verb(4, "AutoMonitor already stopped on call.\n");
  244. break;
  245. }
  246. /*
  247. * Fake start/stop to invoker so will think it did something but
  248. * was already in that mode.
  249. */
  250. if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
  251. ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
  252. }
  253. if (is_monitoring) {
  254. if (!ast_strlen_zero(start_message)) {
  255. ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
  256. }
  257. } else {
  258. if (!ast_strlen_zero(stop_message)) {
  259. ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
  260. }
  261. }
  262. return 0;
  263. }
  264. static void stop_automixmonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *stop_message)
  265. {
  266. ast_verb(4, "AutoMixMonitor used to stop recording call.\n");
  267. if (ast_stop_mixmonitor(peer_chan, NULL)) {
  268. ast_verb(4, "Failed to stop AutoMixMonitor for %s.\n", ast_channel_name(bridge_channel->chan));
  269. if (features_cfg && !(ast_strlen_zero(features_cfg->recordingfailsound))) {
  270. ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
  271. }
  272. return;
  273. }
  274. if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
  275. ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
  276. ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
  277. }
  278. if (!ast_strlen_zero(stop_message)) {
  279. ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
  280. ast_bridge_channel_write_playfile(bridge_channel, NULL, stop_message, NULL);
  281. }
  282. }
  283. static void start_automixmonitor(struct ast_bridge_channel *bridge_channel, struct ast_channel *peer_chan, struct ast_features_general_config *features_cfg, const char *start_message)
  284. {
  285. char *touch_filename;
  286. size_t len;
  287. int x;
  288. enum set_touch_variables_res set_touch_res;
  289. char *caller_chan_id;
  290. char *peer_chan_id;
  291. RAII_VAR(char *, touch_format, NULL, ast_free);
  292. RAII_VAR(char *, touch_monitor, NULL, ast_free);
  293. RAII_VAR(char *, touch_monitor_prefix, NULL, ast_free);
  294. //start by yu.ding
  295. const char * touch_store_num;
  296. touch_store_num = pbx_builtin_getvar_helper(bridge_channel->chan,"TOUCH_STORE_POS");
  297. //end
  298. set_touch_res = set_touch_variables(bridge_channel->chan, 1, &touch_format,
  299. &touch_monitor, &touch_monitor_prefix);
  300. switch (set_touch_res) {
  301. case SET_TOUCH_SUCCESS:
  302. break;
  303. case SET_TOUCH_UNSET:
  304. set_touch_res = set_touch_variables(peer_chan, 1, &touch_format, &touch_monitor,
  305. &touch_monitor_prefix);
  306. if (set_touch_res == SET_TOUCH_ALLOC_FAILURE) {
  307. return;
  308. }
  309. break;
  310. case SET_TOUCH_ALLOC_FAILURE:
  311. return;
  312. }
  313. if (!ast_strlen_zero(touch_monitor)) {
  314. //start by yu.ding
  315. caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid,
  316. ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan)));
  317. len = strlen(touch_monitor) +strlen(caller_chan_id)+ 50;
  318. touch_filename = ast_alloca(len);
  319. snprintf(touch_filename, len, "%s/%s/%s.%s",
  320. S_OR(touch_monitor_prefix, "one_touch"),
  321. // (long) time(NULL),
  322. (touch_store_num!=NULL)?touch_store_num:caller_chan_id,
  323. touch_monitor,
  324. S_OR(touch_format, "wav"));
  325. //end
  326. } else {
  327. caller_chan_id = ast_strdupa(S_COR(ast_channel_caller(bridge_channel->chan)->id.number.valid,
  328. ast_channel_caller(bridge_channel->chan)->id.number.str, ast_channel_name(bridge_channel->chan)));
  329. peer_chan_id = ast_strdupa(S_COR(ast_channel_caller(peer_chan)->id.number.valid,
  330. ast_channel_caller(peer_chan)->id.number.str, ast_channel_name(peer_chan)));
  331. len = strlen(caller_chan_id) + strlen(peer_chan_id) + 50;
  332. touch_filename = ast_alloca(len);
  333. snprintf(touch_filename, len, "%s-%ld-%s-%s.%s",
  334. S_OR(touch_monitor_prefix, "auto"),
  335. (long) time(NULL),
  336. caller_chan_id,
  337. peer_chan_id,
  338. S_OR(touch_format, "wav"));
  339. }
  340. for (x = 0; x < strlen(touch_filename); x++) {
  341. if (touch_filename[x] == '/') {
  342. touch_filename[x] = '-';
  343. }
  344. }
  345. ast_verb(4, "AutoMixMonitor used to record call. Filename: %s\n", touch_filename);
  346. if (ast_start_mixmonitor(peer_chan, touch_filename, "b")) {
  347. ast_verb(4, "AutoMixMonitor feature was tried by '%s' but MixMonitor failed to start.\n",
  348. ast_channel_name(bridge_channel->chan));
  349. if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
  350. ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
  351. }
  352. return;
  353. }
  354. if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
  355. ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
  356. ast_bridge_channel_write_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
  357. }
  358. if (!ast_strlen_zero(start_message)) {
  359. ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
  360. ast_bridge_channel_write_playfile(bridge_channel, NULL, start_message, NULL);
  361. }
  362. pbx_builtin_setvar_helper(bridge_channel->chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
  363. pbx_builtin_setvar_helper(peer_chan, "TOUCH_MIXMONITOR_OUTPUT", touch_filename);
  364. }
  365. static int feature_automixmonitor(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
  366. {
  367. static const char *mixmonitor_spy_type = "MixMonitor";
  368. const char *stop_message;
  369. const char *start_message;
  370. struct ast_bridge_features_automixmonitor *options = hook_pvt;
  371. enum ast_bridge_features_monitor start_stop = options ? options->start_stop : AUTO_MONITOR_TOGGLE;
  372. int is_monitoring;
  373. RAII_VAR(struct ast_channel *, peer_chan, NULL, ast_channel_cleanup);
  374. RAII_VAR(struct ast_features_general_config *, features_cfg, NULL, ao2_cleanup);
  375. ast_channel_lock(bridge_channel->chan);
  376. features_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
  377. ast_channel_unlock(bridge_channel->chan);
  378. ast_bridge_channel_lock_bridge(bridge_channel);
  379. peer_chan = ast_bridge_peer_nolock(bridge_channel->bridge, bridge_channel->chan);
  380. ast_bridge_unlock(bridge_channel->bridge);
  381. if (!peer_chan) {
  382. ast_verb(4, "Cannot start AutoMixMonitor for %s - cannot determine peer in bridge.\n",
  383. ast_channel_name(bridge_channel->chan));
  384. if (features_cfg && !ast_strlen_zero(features_cfg->recordingfailsound)) {
  385. ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->recordingfailsound, NULL);
  386. }
  387. return 0;
  388. }
  389. ast_channel_lock(bridge_channel->chan);
  390. start_message = pbx_builtin_getvar_helper(bridge_channel->chan,
  391. "TOUCH_MIXMONITOR_MESSAGE_START");
  392. start_message = ast_strdupa(S_OR(start_message, ""));
  393. stop_message = pbx_builtin_getvar_helper(bridge_channel->chan,
  394. "TOUCH_MIXMONITOR_MESSAGE_STOP");
  395. stop_message = ast_strdupa(S_OR(stop_message, ""));
  396. ast_channel_unlock(bridge_channel->chan);
  397. is_monitoring =
  398. 0 < ast_channel_audiohook_count_by_source(peer_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
  399. switch (start_stop) {
  400. case AUTO_MONITOR_TOGGLE:
  401. if (is_monitoring) {
  402. stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message);
  403. } else {
  404. start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message);
  405. }
  406. return 0;
  407. case AUTO_MONITOR_START:
  408. if (!is_monitoring) {
  409. start_automixmonitor(bridge_channel, peer_chan, features_cfg, start_message);
  410. return 0;
  411. }
  412. ast_verb(4, "AutoMixMonitor already recording call.\n");
  413. break;
  414. case AUTO_MONITOR_STOP:
  415. if (is_monitoring) {
  416. stop_automixmonitor(bridge_channel, peer_chan, features_cfg, stop_message);
  417. return 0;
  418. }
  419. ast_verb(4, "AutoMixMonitor already stopped on call.\n");
  420. break;
  421. }
  422. /*
  423. * Fake start/stop to invoker so will think it did something but
  424. * was already in that mode.
  425. */
  426. if (features_cfg && !ast_strlen_zero(features_cfg->courtesytone)) {
  427. ast_bridge_channel_queue_playfile(bridge_channel, NULL, features_cfg->courtesytone, NULL);
  428. }
  429. if (is_monitoring) {
  430. if (!ast_strlen_zero(start_message)) {
  431. ast_bridge_channel_queue_playfile(bridge_channel, NULL, start_message, NULL);
  432. }
  433. } else {
  434. if (!ast_strlen_zero(stop_message)) {
  435. ast_bridge_channel_queue_playfile(bridge_channel, NULL, stop_message, NULL);
  436. }
  437. }
  438. return 0;
  439. }
  440. /*! \brief Internal built in feature for hangup */
  441. static int feature_hangup(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
  442. {
  443. /*
  444. * This is very simple, we simply change the state on the
  445. * bridge_channel to force the channel out of the bridge and the
  446. * core takes care of the rest.
  447. */
  448. ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
  449. AST_CAUSE_NORMAL_CLEARING);
  450. return 0;
  451. }
  452. static int unload_module(void)
  453. {
  454. ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_HANGUP);
  455. ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMON);
  456. ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_AUTOMIXMON);
  457. return 0;
  458. }
  459. static int load_module(void)
  460. {
  461. ast_bridge_features_register(AST_BRIDGE_BUILTIN_HANGUP, feature_hangup, NULL);
  462. ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMON, feature_automonitor, NULL);
  463. ast_bridge_features_register(AST_BRIDGE_BUILTIN_AUTOMIXMON, feature_automixmonitor, NULL);
  464. /* This module cannot be unloaded until shutdown */
  465. ast_module_shutdown_ref(ast_module_info->self);
  466. return AST_MODULE_LOAD_SUCCESS;
  467. }
  468. AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Built in bridging features");