codec.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2014, 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 Codecs API
  21. *
  22. * \author Joshua Colp <jcolp@digium.com>
  23. */
  24. /*** MODULEINFO
  25. <support_level>core</support_level>
  26. ***/
  27. #include "asterisk.h"
  28. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  29. #include "asterisk/logger.h"
  30. #include "asterisk/codec.h"
  31. #include "asterisk/format.h"
  32. #include "asterisk/frame.h"
  33. #include "asterisk/astobj2.h"
  34. #include "asterisk/strings.h"
  35. #include "asterisk/module.h"
  36. #include "asterisk/cli.h"
  37. /*! \brief Number of buckets to use for codecs (should be prime for performance reasons) */
  38. #define CODEC_BUCKETS 53
  39. /*! \brief Current identifier value for newly registered codec */
  40. static int codec_id = 1;
  41. /*! \brief Registered codecs */
  42. static struct ao2_container *codecs;
  43. static int codec_hash(const void *obj, int flags)
  44. {
  45. const struct ast_codec *codec;
  46. const char *key;
  47. switch (flags & OBJ_SEARCH_MASK) {
  48. case OBJ_SEARCH_KEY:
  49. key = obj;
  50. return ast_str_hash(key);
  51. case OBJ_SEARCH_OBJECT:
  52. codec = obj;
  53. return ast_str_hash(codec->name);
  54. default:
  55. /* Hash can only work on something with a full key. */
  56. ast_assert(0);
  57. return 0;
  58. }
  59. }
  60. static int codec_cmp(void *obj, void *arg, int flags)
  61. {
  62. const struct ast_codec *left = obj;
  63. const struct ast_codec *right = arg;
  64. const char *right_key = arg;
  65. int cmp;
  66. switch (flags & OBJ_SEARCH_MASK) {
  67. case OBJ_SEARCH_OBJECT:
  68. right_key = right->name;
  69. cmp = strcmp(left->name, right_key);
  70. if (right->type != AST_MEDIA_TYPE_UNKNOWN) {
  71. cmp |= (right->type != left->type);
  72. }
  73. /* BUGBUG: this will allow a match on a codec by name only.
  74. * This is particularly useful when executed by the CLI; if
  75. * that is not needed in translate.c, this can be removed.
  76. */
  77. if (right->sample_rate) {
  78. cmp |= (right->sample_rate != left->sample_rate);
  79. }
  80. break;
  81. case OBJ_SEARCH_KEY:
  82. cmp = strcmp(left->name, right_key);
  83. break;
  84. case OBJ_SEARCH_PARTIAL_KEY:
  85. cmp = strncmp(left->name, right_key, strlen(right_key));
  86. break;
  87. default:
  88. ast_assert(0);
  89. cmp = 0;
  90. break;
  91. }
  92. if (cmp) {
  93. return 0;
  94. }
  95. return CMP_MATCH;
  96. }
  97. static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  98. {
  99. struct ao2_iterator i;
  100. struct ast_codec *codec;
  101. switch (cmd) {
  102. case CLI_INIT:
  103. e->command = "core show codecs [audio|video|image|text]";
  104. e->usage =
  105. "Usage: core show codecs [audio|video|image|text]\n"
  106. " Displays codec mapping\n";
  107. return NULL;
  108. case CLI_GENERATE:
  109. return NULL;
  110. }
  111. if ((a->argc < 3) || (a->argc > 4)) {
  112. return CLI_SHOWUSAGE;
  113. }
  114. if (!ast_opt_dont_warn) {
  115. ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n"
  116. "\tIt does not indicate anything about your configuration.\n");
  117. }
  118. ast_cli(a->fd, "%8s %5s %8s %s\n","ID","TYPE","NAME","DESCRIPTION");
  119. ast_cli(a->fd, "-----------------------------------------------------------------------------------\n");
  120. ao2_rdlock(codecs);
  121. i = ao2_iterator_init(codecs, AO2_ITERATOR_DONTLOCK);
  122. for (; (codec = ao2_iterator_next(&i)); ao2_ref(codec, -1)) {
  123. if (a->argc == 4) {
  124. if (!strcasecmp(a->argv[3], "audio")) {
  125. if (codec->type != AST_MEDIA_TYPE_AUDIO) {
  126. continue;
  127. }
  128. } else if (!strcasecmp(a->argv[3], "video")) {
  129. if (codec->type != AST_MEDIA_TYPE_VIDEO) {
  130. continue;
  131. }
  132. } else if (!strcasecmp(a->argv[3], "image")) {
  133. if (codec->type != AST_MEDIA_TYPE_IMAGE) {
  134. continue;
  135. }
  136. } else if (!strcasecmp(a->argv[3], "text")) {
  137. if (codec->type != AST_MEDIA_TYPE_TEXT) {
  138. continue;
  139. }
  140. } else {
  141. continue;
  142. }
  143. }
  144. ast_cli(a->fd, "%8u %5s %8s (%s)\n",
  145. codec->id,
  146. ast_codec_media_type2str(codec->type),
  147. codec->name,
  148. codec->description);
  149. }
  150. ao2_iterator_destroy(&i);
  151. ao2_unlock(codecs);
  152. return CLI_SUCCESS;
  153. }
  154. /*! \brief Callback function for getting a codec based on unique identifier */
  155. static int codec_id_cmp(void *obj, void *arg, int flags)
  156. {
  157. struct ast_codec *codec = obj;
  158. int *id = arg;
  159. return (codec->id == *id) ? CMP_MATCH | CMP_STOP : 0;
  160. }
  161. static char *show_codec(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  162. {
  163. int type_punned_codec;
  164. struct ast_codec *codec;
  165. switch (cmd) {
  166. case CLI_INIT:
  167. e->command = "core show codec";
  168. e->usage =
  169. "Usage: core show codec <number>\n"
  170. " Displays codec mapping\n";
  171. return NULL;
  172. case CLI_GENERATE:
  173. return NULL;
  174. }
  175. if (a->argc != 4) {
  176. return CLI_SHOWUSAGE;
  177. }
  178. if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) {
  179. return CLI_SHOWUSAGE;
  180. }
  181. codec = ao2_callback(codecs, 0, codec_id_cmp, &type_punned_codec);
  182. if (!codec) {
  183. ast_cli(a->fd, "Codec %d not found\n", type_punned_codec);
  184. return CLI_SUCCESS;
  185. }
  186. ast_cli(a->fd, "%11u %s\n", (unsigned int) codec->id, codec->description);
  187. ao2_ref(codec, -1);
  188. return CLI_SUCCESS;
  189. }
  190. /* Builtin Asterisk CLI-commands for debugging */
  191. static struct ast_cli_entry codec_cli[] = {
  192. AST_CLI_DEFINE(show_codecs, "Displays a list of registered codecs"),
  193. AST_CLI_DEFINE(show_codec, "Shows a specific codec"),
  194. };
  195. /*! \brief Function called when the process is shutting down */
  196. static void codec_shutdown(void)
  197. {
  198. ast_cli_unregister_multiple(codec_cli, ARRAY_LEN(codec_cli));
  199. ao2_cleanup(codecs);
  200. codecs = NULL;
  201. }
  202. int ast_codec_init(void)
  203. {
  204. codecs = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, CODEC_BUCKETS, codec_hash, codec_cmp);
  205. if (!codecs) {
  206. return -1;
  207. }
  208. ast_cli_register_multiple(codec_cli, ARRAY_LEN(codec_cli));
  209. ast_register_cleanup(codec_shutdown);
  210. return 0;
  211. }
  212. static void codec_dtor(void *obj)
  213. {
  214. struct ast_codec *codec;
  215. codec = obj;
  216. ast_module_unref(codec->mod);
  217. }
  218. int __ast_codec_register(struct ast_codec *codec, struct ast_module *mod)
  219. {
  220. SCOPED_AO2WRLOCK(lock, codecs);
  221. struct ast_codec *codec_new;
  222. /* Some types have specific requirements */
  223. if (codec->type == AST_MEDIA_TYPE_UNKNOWN) {
  224. ast_log(LOG_ERROR, "A media type must be specified for codec '%s'\n", codec->name);
  225. return -1;
  226. } else if (codec->type == AST_MEDIA_TYPE_AUDIO) {
  227. if (!codec->sample_rate) {
  228. ast_log(LOG_ERROR, "A sample rate must be specified for codec '%s' of type '%s'\n",
  229. codec->name, ast_codec_media_type2str(codec->type));
  230. return -1;
  231. }
  232. }
  233. codec_new = ao2_find(codecs, codec, OBJ_SEARCH_OBJECT | OBJ_NOLOCK);
  234. if (codec_new) {
  235. ast_log(LOG_ERROR, "A codec with name '%s' of type '%s' and sample rate '%u' is already registered\n",
  236. codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
  237. ao2_ref(codec_new, -1);
  238. return -1;
  239. }
  240. codec_new = ao2_t_alloc_options(sizeof(*codec_new), codec_dtor,
  241. AO2_ALLOC_OPT_LOCK_NOLOCK, S_OR(codec->description, ""));
  242. if (!codec_new) {
  243. ast_log(LOG_ERROR, "Could not allocate a codec with name '%s' of type '%s' and sample rate '%u'\n",
  244. codec->name, ast_codec_media_type2str(codec->type), codec->sample_rate);
  245. return -1;
  246. }
  247. *codec_new = *codec;
  248. codec_new->id = codec_id++;
  249. ao2_link_flags(codecs, codec_new, OBJ_NOLOCK);
  250. /* Once registered a codec can not be unregistered, and the module must persist until shutdown */
  251. ast_module_shutdown_ref(mod);
  252. ast_verb(2, "Registered '%s' codec '%s' at sample rate '%u' with id '%u'\n",
  253. ast_codec_media_type2str(codec->type), codec->name, codec->sample_rate, codec_new->id);
  254. ao2_ref(codec_new, -1);
  255. return 0;
  256. }
  257. struct ast_codec *ast_codec_get(const char *name, enum ast_media_type type, unsigned int sample_rate)
  258. {
  259. struct ast_codec codec = {
  260. .name = name,
  261. .type = type,
  262. .sample_rate = sample_rate,
  263. };
  264. return ao2_find(codecs, &codec, OBJ_SEARCH_OBJECT);
  265. }
  266. struct ast_codec *ast_codec_get_by_id(int id)
  267. {
  268. return ao2_callback(codecs, 0, codec_id_cmp, &id);
  269. }
  270. int ast_codec_get_max(void)
  271. {
  272. return codec_id;
  273. }
  274. const char *ast_codec_media_type2str(enum ast_media_type type)
  275. {
  276. switch (type) {
  277. case AST_MEDIA_TYPE_AUDIO:
  278. return "audio";
  279. case AST_MEDIA_TYPE_VIDEO:
  280. return "video";
  281. case AST_MEDIA_TYPE_IMAGE:
  282. return "image";
  283. case AST_MEDIA_TYPE_TEXT:
  284. return "text";
  285. default:
  286. return "<unknown>";
  287. }
  288. }
  289. unsigned int ast_codec_samples_count(struct ast_frame *frame)
  290. {
  291. struct ast_codec *codec;
  292. unsigned int samples = 0;
  293. if ((frame->frametype != AST_FRAME_VOICE) &&
  294. (frame->frametype != AST_FRAME_VIDEO) &&
  295. (frame->frametype != AST_FRAME_IMAGE)) {
  296. return 0;
  297. }
  298. codec = ast_format_get_codec(frame->subclass.format);
  299. if (codec->samples_count) {
  300. samples = codec->samples_count(frame);
  301. } else {
  302. ast_log(LOG_WARNING, "Unable to calculate samples for codec %s\n",
  303. ast_format_get_name(frame->subclass.format));
  304. }
  305. ao2_ref(codec, -1);
  306. return samples;
  307. }
  308. unsigned int ast_codec_determine_length(const struct ast_codec *codec, unsigned int samples)
  309. {
  310. if (!codec->get_length) {
  311. return 0;
  312. }
  313. return codec->get_length(samples);
  314. }