framehook.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2010, Digium, Inc.
  5. *
  6. * David Vossel <dvossel@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 FrameHooks Architecture
  21. *
  22. * \author David Vossel <dvossel@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/channel.h"
  30. #include "asterisk/linkedlists.h"
  31. #include "asterisk/framehook.h"
  32. #include "asterisk/frame.h"
  33. struct ast_framehook {
  34. struct ast_framehook_interface i;
  35. /*! This pointer to ast_channel the framehook is attached to. */
  36. struct ast_channel *chan;
  37. /*! the id representing this framehook on a channel */
  38. unsigned int id;
  39. /*! when set, this signals the read and write function to detach the hook */
  40. int detach_and_destroy_me;
  41. /*! list entry for ast_framehook_list object */
  42. AST_LIST_ENTRY(ast_framehook) list;
  43. };
  44. struct ast_framehook_list {
  45. /*! the number of hooks currently present */
  46. unsigned int count;
  47. /*! id for next framehook added */
  48. unsigned int id_count;
  49. AST_LIST_HEAD_NOLOCK(, ast_framehook) list;
  50. };
  51. static void framehook_detach_and_destroy(struct ast_framehook *framehook)
  52. {
  53. struct ast_frame *frame;
  54. frame = framehook->i.event_cb(framehook->chan, NULL, AST_FRAMEHOOK_EVENT_DETACHED, framehook->i.data);
  55. /* never assume anything about this function. If you can return a frame during
  56. * the detached event, then assume someone will. */
  57. if (frame) {
  58. ast_frfree(frame);
  59. }
  60. framehook->chan = NULL;
  61. if (framehook->i.destroy_cb) {
  62. framehook->i.destroy_cb(framehook->i.data);
  63. }
  64. ast_free(framehook);
  65. }
  66. static struct ast_frame *framehook_list_push_event(struct ast_framehook_list *framehooks, struct ast_frame *frame, enum ast_framehook_event event)
  67. {
  68. struct ast_framehook *framehook;
  69. struct ast_frame *original_frame;
  70. int *skip;
  71. size_t skip_size;
  72. if (!framehooks) {
  73. return frame;
  74. }
  75. skip_size = sizeof(int) * framehooks->count;
  76. skip = alloca(skip_size);
  77. memset(skip, 0, skip_size);
  78. do {
  79. unsigned int num = 0;
  80. original_frame = frame;
  81. AST_LIST_TRAVERSE_SAFE_BEGIN(&framehooks->list, framehook, list) {
  82. if (framehook->detach_and_destroy_me) {
  83. /* this guy is signaled for destruction */
  84. AST_LIST_REMOVE_CURRENT(list);
  85. framehook_detach_and_destroy(framehook);
  86. continue;
  87. }
  88. /* If this framehook has been marked as needing to be skipped, do so */
  89. if (skip[num]) {
  90. num++;
  91. continue;
  92. }
  93. frame = framehook->i.event_cb(framehook->chan, frame, event, framehook->i.data);
  94. if (frame != original_frame) {
  95. /* To prevent looping we skip any framehooks that have already provided a modified frame */
  96. skip[num] = 1;
  97. break;
  98. }
  99. num++;
  100. }
  101. AST_LIST_TRAVERSE_SAFE_END;
  102. } while (frame != original_frame);
  103. return frame;
  104. }
  105. int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interface *i)
  106. {
  107. struct ast_framehook *framehook;
  108. struct ast_framehook_list *fh_list;
  109. struct ast_frame *frame;
  110. if (i->version != AST_FRAMEHOOK_INTERFACE_VERSION) {
  111. ast_log(LOG_ERROR, "Version '%hu' of framehook interface not what we compiled against (%hu)\n",
  112. i->version, AST_FRAMEHOOK_INTERFACE_VERSION);
  113. return -1;
  114. }
  115. if (!i->event_cb || !(framehook = ast_calloc(1, sizeof(*framehook)))) {
  116. return -1;
  117. }
  118. framehook->i = *i;
  119. framehook->chan = chan;
  120. /* create the framehook list if it didn't already exist */
  121. if (!ast_channel_framehooks(chan)) {
  122. if (!(fh_list = ast_calloc(1, sizeof(*ast_channel_framehooks(chan))))) {
  123. ast_free(framehook);
  124. return -1;
  125. }
  126. ast_channel_framehooks_set(chan, fh_list);
  127. }
  128. ast_channel_framehooks(chan)->count++;
  129. framehook->id = ++ast_channel_framehooks(chan)->id_count;
  130. AST_LIST_INSERT_TAIL(&ast_channel_framehooks(chan)->list, framehook, list);
  131. /* Tell the event callback we're live and rocking */
  132. frame = framehook->i.event_cb(framehook->chan, NULL, AST_FRAMEHOOK_EVENT_ATTACHED, framehook->i.data);
  133. /* Never assume anything about this function. If you can return a frame during
  134. * the attached event, then assume someone will. */
  135. if (frame) {
  136. ast_frfree(frame);
  137. }
  138. return framehook->id;
  139. }
  140. int ast_framehook_detach(struct ast_channel *chan, int id)
  141. {
  142. struct ast_framehook *framehook;
  143. int res = -1;
  144. if (!ast_channel_framehooks(chan)) {
  145. return res;
  146. }
  147. AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(chan)->list, framehook, list) {
  148. if (framehook->id == id) {
  149. /* we mark for detachment rather than doing explicitly here because
  150. * it needs to be safe for this function to be called within the
  151. * event callback. If we allowed the hook to actually be destroyed
  152. * immediately here, the event callback would crash on exit. */
  153. framehook->detach_and_destroy_me = 1;
  154. res = 0;
  155. break;
  156. }
  157. }
  158. AST_LIST_TRAVERSE_SAFE_END;
  159. return res;
  160. }
  161. int ast_framehook_list_destroy(struct ast_channel *chan)
  162. {
  163. struct ast_framehook *framehook;
  164. if (!ast_channel_framehooks(chan)) {
  165. return 0;
  166. }
  167. AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(chan)->list, framehook, list) {
  168. AST_LIST_REMOVE_CURRENT(list);
  169. framehook_detach_and_destroy(framehook);
  170. }
  171. AST_LIST_TRAVERSE_SAFE_END;
  172. ast_free(ast_channel_framehooks(chan));
  173. ast_channel_framehooks_set(chan, NULL);
  174. return 0;
  175. }
  176. int ast_framehook_list_is_empty(struct ast_framehook_list *framehooks)
  177. {
  178. if (!framehooks) {
  179. return 1;
  180. }
  181. return AST_LIST_EMPTY(&framehooks->list) ? 1 : 0;
  182. }
  183. int ast_framehook_list_contains_no_active(struct ast_framehook_list *framehooks)
  184. {
  185. struct ast_framehook *cur;
  186. if (!framehooks) {
  187. return 1;
  188. }
  189. if (AST_LIST_EMPTY(&framehooks->list)) {
  190. return 1;
  191. }
  192. AST_LIST_TRAVERSE(&framehooks->list, cur, list) {
  193. if (cur->detach_and_destroy_me) {
  194. continue;
  195. }
  196. return 0;
  197. }
  198. return 1;
  199. }
  200. struct ast_frame *ast_framehook_list_write_event(struct ast_framehook_list *framehooks, struct ast_frame *frame)
  201. {
  202. return framehook_list_push_event(framehooks, frame, AST_FRAMEHOOK_EVENT_WRITE);
  203. }
  204. struct ast_frame *ast_framehook_list_read_event(struct ast_framehook_list *framehooks, struct ast_frame *frame)
  205. {
  206. return framehook_list_push_event(framehooks, frame, AST_FRAMEHOOK_EVENT_READ);
  207. }