sounds.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2013, Digium, Inc.
  5. *
  6. * Kinsey Moore <markster@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. * \brief Sound file format and description index.
  20. */
  21. #include "asterisk.h"
  22. #include <dirent.h>
  23. #include <sys/stat.h>
  24. #include "asterisk/utils.h"
  25. #include "asterisk/lock.h"
  26. #include "asterisk/format.h"
  27. #include "asterisk/format_cap.h"
  28. #include "asterisk/paths.h" /* use ast_config_AST_DATA_DIR */
  29. #include "asterisk/media_index.h"
  30. #include "asterisk/sounds_index.h"
  31. #include "asterisk/file.h"
  32. #include "asterisk/cli.h"
  33. #include "asterisk/_private.h"
  34. #include "asterisk/stasis_message_router.h"
  35. #include "asterisk/stasis_system.h"
  36. /*** MODULEINFO
  37. <support_level>core</support_level>
  38. ***/
  39. /*! \brief The number of buckets to be used for storing language-keyed objects */
  40. #define LANGUAGE_BUCKETS 7
  41. /*! \brief Get the languages in which sound files are available */
  42. static struct ao2_container *get_languages(void)
  43. {
  44. RAII_VAR(struct ao2_container *, lang_dirs, NULL, ao2_cleanup);
  45. struct dirent* dent;
  46. DIR* srcdir;
  47. RAII_VAR(struct ast_str *, media_dir, ast_str_create(64), ast_free);
  48. RAII_VAR(struct ast_str *, variant_dir, ast_str_create(64), ast_free);
  49. lang_dirs = ast_str_container_alloc(LANGUAGE_BUCKETS);
  50. if (!media_dir || !lang_dirs) {
  51. return NULL;
  52. }
  53. ast_str_set(&media_dir, 0, "%s/sounds", ast_config_AST_DATA_DIR);
  54. srcdir = opendir(ast_str_buffer(media_dir));
  55. if (srcdir == NULL) {
  56. ast_log(LOG_ERROR, "Failed to open %s\n", ast_str_buffer(media_dir));
  57. return NULL;
  58. }
  59. while((dent = readdir(srcdir)) != NULL) {
  60. struct stat st;
  61. if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) {
  62. continue;
  63. }
  64. ast_str_reset(variant_dir);
  65. ast_str_set(&variant_dir, 0, "%s/%s", ast_str_buffer(media_dir), dent->d_name);
  66. if (stat(ast_str_buffer(variant_dir), &st) < 0) {
  67. ast_log(LOG_ERROR, "Failed to stat %s\n", ast_str_buffer(variant_dir));
  68. continue;
  69. }
  70. if (S_ISDIR(st.st_mode)) {
  71. ast_str_container_add(lang_dirs, dent->d_name);
  72. }
  73. }
  74. closedir(srcdir);
  75. ao2_ref(lang_dirs, +1);
  76. return lang_dirs;
  77. }
  78. int ast_sounds_reindex(void)
  79. {
  80. return 0;
  81. }
  82. static int show_sounds_cb(void *obj, void *arg, int flags)
  83. {
  84. char *name = obj;
  85. struct ast_cli_args *a = arg;
  86. ast_cli(a->fd, "%s\n", name);
  87. return 0;
  88. }
  89. static int show_sound_info_cb(void *obj, void *arg, void *data, int flags)
  90. {
  91. char *language = obj;
  92. struct ast_cli_args *a = arg;
  93. struct ast_format *format;
  94. int formats_shown = 0;
  95. struct ast_media_index *local_index = data;
  96. struct ast_format_cap *cap;
  97. const char *description = ast_media_get_description(local_index, a->argv[3], language);
  98. ast_cli(a->fd, " Language %s:\n", language);
  99. if (!ast_strlen_zero(description)) {
  100. ast_cli(a->fd, " Description: %s\n", description);
  101. }
  102. cap = ast_media_get_format_cap(local_index, a->argv[3], language);
  103. if (cap) {
  104. int x;
  105. for (x = 0; x < ast_format_cap_count(cap); x++) {
  106. format = ast_format_cap_get_format(cap, x);
  107. ast_cli(a->fd, " Format: %s\n", ast_format_get_name(format));
  108. ao2_ref(format, -1);
  109. formats_shown = 1;
  110. }
  111. ao2_ref(cap, -1);
  112. }
  113. if (!formats_shown) {
  114. ast_cli(a->fd, " No Formats Available\n");
  115. }
  116. return 0;
  117. }
  118. /*! \brief Show a list of sounds available on the system */
  119. static char *handle_cli_sounds_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  120. {
  121. switch (cmd) {
  122. case CLI_INIT:
  123. e->command = "core show sounds";
  124. e->usage =
  125. "Usage: core show sounds\n"
  126. " Shows a listing of sound files available on the system.\n";
  127. return NULL;
  128. case CLI_GENERATE:
  129. return NULL;
  130. }
  131. if (a->argc == 3) {
  132. struct ast_media_index *sounds_index = ast_sounds_get_index();
  133. struct ao2_container *sound_files;
  134. if (!sounds_index) {
  135. return CLI_FAILURE;
  136. }
  137. sound_files = ast_media_get_media(sounds_index);
  138. ao2_ref(sounds_index, -1);
  139. if (!sound_files) {
  140. return CLI_FAILURE;
  141. }
  142. ast_cli(a->fd, "Available audio files:\n");
  143. ao2_callback(sound_files, OBJ_MULTIPLE | OBJ_NODATA, show_sounds_cb, a);
  144. ao2_ref(sound_files, -1);
  145. return CLI_SUCCESS;
  146. }
  147. return CLI_SHOWUSAGE;
  148. }
  149. /*! \brief Show details about a sound available in the system */
  150. static char *handle_cli_sound_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  151. {
  152. int length;
  153. struct ao2_iterator it_sounds;
  154. char *filename;
  155. struct ast_media_index *sounds_index;
  156. struct ao2_container *sound_files;
  157. switch (cmd) {
  158. case CLI_INIT:
  159. e->command = "core show sound";
  160. e->usage =
  161. "Usage: core show sound [soundid]\n"
  162. " Shows information about the specified sound.\n";
  163. return NULL;
  164. case CLI_GENERATE:
  165. if (a->pos != 3) {
  166. return NULL;
  167. }
  168. sounds_index = ast_sounds_get_index();
  169. if (!sounds_index) {
  170. return NULL;
  171. }
  172. sound_files = ast_media_get_media(sounds_index);
  173. ao2_ref(sounds_index, -1);
  174. if (!sound_files) {
  175. return NULL;
  176. }
  177. length = strlen(a->word);
  178. it_sounds = ao2_iterator_init(sound_files, 0);
  179. while ((filename = ao2_iterator_next(&it_sounds))) {
  180. if (!strncasecmp(a->word, filename, length)) {
  181. if (ast_cli_completion_add(ast_strdup(filename))) {
  182. ao2_ref(filename, -1);
  183. break;
  184. }
  185. }
  186. ao2_ref(filename, -1);
  187. }
  188. ao2_iterator_destroy(&it_sounds);
  189. ao2_ref(sound_files, -1);
  190. return NULL;
  191. }
  192. if (a->argc == 4) {
  193. struct ao2_container *variants;
  194. sounds_index = ast_sounds_get_index_for_file(a->argv[3]);
  195. if (!sounds_index) {
  196. return NULL;
  197. }
  198. variants = ast_media_get_variants(sounds_index, a->argv[3]);
  199. if (!variants || !ao2_container_count(variants)) {
  200. ao2_ref(sounds_index, -1);
  201. ao2_cleanup(variants);
  202. ast_cli(a->fd, "ERROR: File %s not found in index\n", a->argv[3]);
  203. return CLI_FAILURE;
  204. }
  205. ast_cli(a->fd, "Indexed Information for %s:\n", a->argv[3]);
  206. ao2_callback_data(variants, OBJ_MULTIPLE | OBJ_NODATA, show_sound_info_cb, a, sounds_index);
  207. ao2_ref(sounds_index, -1);
  208. ao2_ref(variants, -1);
  209. return CLI_SUCCESS;
  210. }
  211. return CLI_SHOWUSAGE;
  212. }
  213. /*! \brief Struct for registering CLI commands */
  214. static struct ast_cli_entry cli_sounds[] = {
  215. AST_CLI_DEFINE(handle_cli_sounds_show, "Shows available sounds"),
  216. AST_CLI_DEFINE(handle_cli_sound_show, "Shows details about a specific sound"),
  217. };
  218. static void sounds_cleanup(void)
  219. {
  220. ast_cli_unregister_multiple(cli_sounds, ARRAY_LEN(cli_sounds));
  221. }
  222. int ast_sounds_index_init(void)
  223. {
  224. int res = 0;
  225. res = ast_cli_register_multiple(cli_sounds, ARRAY_LEN(cli_sounds));
  226. if (res) {
  227. return 1;
  228. }
  229. ast_register_cleanup(sounds_cleanup);
  230. return 0;
  231. }
  232. /*! \brief Callback to process an individual language directory or subdirectory */
  233. static int update_index_cb(void *obj, void *arg, void *data, int flags)
  234. {
  235. char *lang = obj;
  236. char *filename = data;
  237. struct ast_media_index *index = arg;
  238. if (ast_media_index_update_for_file(index, lang, filename)) {
  239. return CMP_MATCH;
  240. }
  241. return 0;
  242. }
  243. struct ast_media_index *ast_sounds_get_index(void)
  244. {
  245. return ast_sounds_get_index_for_file(NULL);
  246. }
  247. struct ast_media_index *ast_sounds_get_index_for_file(const char *filename)
  248. {
  249. struct ast_str *sounds_dir = ast_str_create(64);
  250. struct ao2_container *languages;
  251. char *failed_index;
  252. struct ast_media_index *new_index;
  253. if (!sounds_dir) {
  254. return NULL;
  255. }
  256. ast_str_set(&sounds_dir, 0, "%s/sounds", ast_config_AST_DATA_DIR);
  257. new_index = ast_media_index_create(ast_str_buffer(sounds_dir));
  258. ast_free(sounds_dir);
  259. if (!new_index) {
  260. return NULL;
  261. }
  262. languages = get_languages();
  263. if (!languages) {
  264. ao2_ref(new_index, -1);
  265. return NULL;
  266. }
  267. failed_index = ao2_callback_data(languages, 0, update_index_cb, new_index, (void *)filename);
  268. ao2_ref(languages, -1);
  269. if (failed_index) {
  270. ao2_ref(failed_index, -1);
  271. ao2_ref(new_index, -1);
  272. new_index = NULL;
  273. }
  274. return new_index;
  275. }