#include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
#include "asterisk/channel.h"
#include "asterisk/linkedlists.h"
#include "asterisk/framehook.h"
};
struct ast_framehook_list {
+ /*! the number of hooks currently present */
+ unsigned int count;
+ /*! id for next framehook added */
unsigned int id_count;
AST_LIST_HEAD_NOLOCK(, ast_framehook) list;
};
-static void framehook_detach_and_destroy(struct ast_framehook *framehook)
+enum framehook_detachment_mode
+{
+ /*! Destroy the framehook outright. */
+ FRAMEHOOK_DETACH_DESTROY = 0,
+ /*! Remove the framehook from the channel, but don't destroy the data since
+ * it will be used by a replacement framehook on another channel. */
+ FRAMEHOOK_DETACH_PRESERVE,
+};
+
+static void framehook_detach(struct ast_framehook *framehook, enum framehook_detachment_mode mode)
{
struct ast_frame *frame;
frame = framehook->i.event_cb(framehook->chan, NULL, AST_FRAMEHOOK_EVENT_DETACHED, framehook->i.data);
}
framehook->chan = NULL;
- if (framehook->i.destroy_cb) {
+ if (mode == FRAMEHOOK_DETACH_DESTROY && framehook->i.destroy_cb) {
framehook->i.destroy_cb(framehook->i.data);
}
ast_free(framehook);
static struct ast_frame *framehook_list_push_event(struct ast_framehook_list *framehooks, struct ast_frame *frame, enum ast_framehook_event event)
{
struct ast_framehook *framehook;
+ struct ast_frame *original_frame;
+ int *skip;
+ size_t skip_size;
if (!framehooks) {
return frame;
}
- AST_LIST_TRAVERSE_SAFE_BEGIN(&framehooks->list, framehook, list) {
- if (framehook->detach_and_destroy_me) {
- /* this guy is signaled for destruction */
- AST_LIST_REMOVE_CURRENT(list);
- framehook_detach_and_destroy(framehook);
- } else {
+ skip_size = sizeof(int) * framehooks->count;
+ skip = ast_alloca(skip_size);
+ memset(skip, 0, skip_size);
+
+ do {
+ unsigned int num = 0;
+ original_frame = frame;
+
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&framehooks->list, framehook, list) {
+ if (framehook->detach_and_destroy_me) {
+ /* this guy is signaled for destruction */
+ AST_LIST_REMOVE_CURRENT(list);
+ framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
+ continue;
+ }
+
+ /* If this framehook has been marked as needing to be skipped, do so */
+ if (skip[num]) {
+ num++;
+ continue;
+ }
+
frame = framehook->i.event_cb(framehook->chan, frame, event, framehook->i.data);
+
+ if (frame != original_frame) {
+ /* To prevent looping we skip any framehooks that have already provided a modified frame */
+ skip[num] = 1;
+ break;
+ }
+
+ num++;
}
- }
- AST_LIST_TRAVERSE_SAFE_END;
+ AST_LIST_TRAVERSE_SAFE_END;
+ } while (frame != original_frame);
+
return frame;
}
struct ast_framehook_list *fh_list;
struct ast_frame *frame;
if (i->version != AST_FRAMEHOOK_INTERFACE_VERSION) {
- ast_log(LOG_ERROR, "Version '%hu' of framehook interface not what we compiled against (%hu)\n",
+ ast_log(LOG_ERROR, "Version '%hu' of framehook interface not what we compiled against (%i)\n",
i->version, AST_FRAMEHOOK_INTERFACE_VERSION);
return -1;
}
ast_channel_framehooks_set(chan, fh_list);
}
+ ast_channel_framehooks(chan)->count++;
framehook->id = ++ast_channel_framehooks(chan)->id_count;
AST_LIST_INSERT_TAIL(&ast_channel_framehooks(chan)->list, framehook, list);
ast_frfree(frame);
}
+ if (ast_channel_is_bridged(chan)) {
+ ast_channel_set_unbridged_nolock(chan, 1);
+ }
+
return framehook->id;
}
}
AST_LIST_TRAVERSE_SAFE_END;
+ if (!res && ast_channel_is_bridged(chan)) {
+ ast_channel_set_unbridged_nolock(chan, 1);
+ }
+
return res;
}
}
AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(chan)->list, framehook, list) {
AST_LIST_REMOVE_CURRENT(list);
- framehook_detach_and_destroy(framehook);
+ framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
}
AST_LIST_TRAVERSE_SAFE_END;
ast_free(ast_channel_framehooks(chan));
return 0;
}
+void ast_framehook_list_fixup(struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+ struct ast_framehook *framehook;
+ int moved_framehook_id;
+
+ if (ast_channel_framehooks(new_chan)) {
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&ast_channel_framehooks(new_chan)->list, framehook, list) {
+ if (framehook->i.disable_inheritance) {
+ ast_framehook_detach(new_chan, framehook->id);
+ continue;
+ }
+
+ if (framehook->i.chan_breakdown_cb) {
+ framehook->i.chan_breakdown_cb(framehook->i.data, framehook->id,
+ old_chan, new_chan);
+ }
+ }
+ AST_LIST_TRAVERSE_SAFE_END;
+ }
+
+ if (!ast_channel_framehooks(old_chan)) {
+ return;
+ }
+
+ if (!AST_LIST_EMPTY(&ast_channel_framehooks(old_chan)->list)
+ && ast_channel_is_bridged(old_chan)) {
+ ast_channel_set_unbridged_nolock(old_chan, 1);
+ }
+ while ((framehook = AST_LIST_REMOVE_HEAD(&ast_channel_framehooks(old_chan)->list, list))) {
+ /* If inheritance is not allowed for this framehook, just destroy it. */
+ if (framehook->i.disable_inheritance) {
+ framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
+ continue;
+ }
+
+ /* Otherwise move it to the other channel and perform any fixups set by the framehook interface */
+ moved_framehook_id = ast_framehook_attach(new_chan, &framehook->i);
+ if (moved_framehook_id < 0) {
+ ast_log(LOG_WARNING, "Failed framehook copy during masquerade. Expect loss of features.\n");
+ framehook_detach(framehook, FRAMEHOOK_DETACH_DESTROY);
+ } else {
+ if (framehook->i.chan_fixup_cb) {
+ framehook->i.chan_fixup_cb(framehook->i.data, moved_framehook_id,
+ old_chan, new_chan);
+ }
+
+ framehook_detach(framehook, FRAMEHOOK_DETACH_PRESERVE);
+ }
+ }
+}
+
int ast_framehook_list_is_empty(struct ast_framehook_list *framehooks)
{
if (!framehooks) {
int ast_framehook_list_contains_no_active(struct ast_framehook_list *framehooks)
{
+ return ast_framehook_list_contains_no_active_of_type(framehooks, 0);
+}
+
+int ast_framehook_list_contains_no_active_of_type(struct ast_framehook_list *framehooks,
+ enum ast_frame_type type)
+{
struct ast_framehook *cur;
if (!framehooks) {
if (cur->detach_and_destroy_me) {
continue;
}
+ if (type && cur->i.consume_cb && !cur->i.consume_cb(cur->i.data, type)) {
+ continue;
+ }
return 0;
}