codec.c 10 KB

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