From d134150be28e9016131b4c0b5c9273ff65cf2084 Mon Sep 17 00:00:00 2001 From: Joshua Colp Date: Sun, 11 May 2014 02:09:10 +0000 Subject: framehooks: Add callback for determining if a hook is consuming frames of a specific type. In the past framehooks have had no capability to determine what frame types a hook is actually interested in consuming. This has meant that code has had to assume they want all frames, thus preventing native bridging. This change adds a callback which allows a framehook to be queried for whether it is consuming a frame of a specific type. The native RTP bridging module has also been updated to take advantange of this, allowing native bridging to occur when previously it would not. ASTERISK-23497 #comment Reported by: Etienne Lessard ASTERISK-23497 #close Review: https://reviewboard.asterisk.org/r/3522/ ........ Merged revisions 413681 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@413682 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- bridges/bridge_native_rtp.c | 9 +- include/asterisk/channel.h | 14 ++- include/asterisk/framehook.h | 222 +++++++++++++++++++++++++------------------ main/bridge_basic.c | 7 ++ main/channel.c | 7 ++ main/framehook.c | 13 +++ 6 files changed, 174 insertions(+), 98 deletions(-) diff --git a/bridges/bridge_native_rtp.c b/bridges/bridge_native_rtp.c index 02b094b31..7e48ab92f 100644 --- a/bridges/bridge_native_rtp.c +++ b/bridges/bridge_native_rtp.c @@ -288,10 +288,16 @@ static struct ast_frame *native_rtp_framehook(struct ast_channel *chan, struct a return f; } +/*! \brief Callback function which informs upstream if we are consuming a frame of a specific type */ +static int native_rtp_framehook_consume(void *data, enum ast_frame_type type) +{ + return (type == AST_FRAME_CONTROL ? 1 : 0); +} + /*! \brief Internal helper function which checks whether the channels are compatible with our native bridging */ static int native_rtp_bridge_capable(struct ast_channel *chan) { - return !ast_channel_has_audio_frame_or_monitor(chan); + return !ast_channel_has_hook_requiring_audio(chan); } static int native_rtp_bridge_compatible(struct ast_bridge *bridge) @@ -392,6 +398,7 @@ static int native_rtp_bridge_framehook_attach(struct ast_bridge_channel *bridge_ static struct ast_framehook_interface hook = { .version = AST_FRAMEHOOK_INTERFACE_VERSION, .event_cb = native_rtp_framehook, + .consume_cb = native_rtp_framehook_consume, }; if (!data) { diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 8cef68e87..a4ad2ae01 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -1049,8 +1049,8 @@ enum { */ AST_SOFTHANGUP_EXPLICIT = (1 << 5), /*! - * Used to break a bridge so the channel can be spied upon - * instead of actually hanging up. + * Used to request that the bridge core re-evaluate the current + * bridging technology in use by the bridge this channel is in. */ AST_SOFTHANGUP_UNBRIDGE = (1 << 6), /*! @@ -4401,6 +4401,16 @@ const char *ast_channel_oldest_linkedid(const char *a, const char *b); */ int ast_channel_has_audio_frame_or_monitor(struct ast_channel *chan); +/*! + * \brief Check if the channel has any active hooks that require audio. + * \since 12.3.0 + * + * \param chan The channel to check. + * + * \retval non-zero if channel has active audiohooks, audio framehooks, or monitor. + */ +int ast_channel_has_hook_requiring_audio(struct ast_channel *chan); + /*! * \brief Removes the trailing identifiers from a channel name string * \since 12.0.0 diff --git a/include/asterisk/framehook.h b/include/asterisk/framehook.h index 9f1bdab17..f33927c7b 100644 --- a/include/asterisk/framehook.h +++ b/include/asterisk/framehook.h @@ -25,114 +25,114 @@ \page AstFrameHookAPI Asterisk FrameHook API \section FrameHookFunctionality How FrameHooks Work - FrameHooks work by intercepting all frames being written and read off - a channel and allowing those frames to be viewed and manipulated within a - call back function. Frame interception occurs before any processing is - done on the frame, which means this hook can be used to transparently - manipulate a frame before it is read from the channel or written - to the tech_pvt. This API can be thought of as a layer between the - channel API and the Asterisk core when going in the READ direction, and - as a layer between the Channel API and the tech_pvt when going in the - WRITE direction. + FrameHooks work by intercepting all frames being written and read off + a channel and allowing those frames to be viewed and manipulated within a + call back function. Frame interception occurs before any processing is + done on the frame, which means this hook can be used to transparently + manipulate a frame before it is read from the channel or written + to the tech_pvt. This API can be thought of as a layer between the + channel API and the Asterisk core when going in the READ direction, and + as a layer between the Channel API and the tech_pvt when going in the + WRITE direction. \section FrameHookAPIUsage How to Use an FrameHook - Attaching and detaching an FrameHook to a channel is very simple. There are only - two functions involved, ast_framehook_attach() which will return an id representing - the new FrameHook on the channel, and ast_framehook_detach() which signals the - FrameHook for detachment and destruction. Below is detailed information each of these - functions and their usage. + Attaching and detaching an FrameHook to a channel is very simple. There are only + two functions involved, ast_framehook_attach() which will return an id representing + the new FrameHook on the channel, and ast_framehook_detach() which signals the + FrameHook for detachment and destruction. Below is detailed information each of these + functions and their usage. \code - struct ast_framehook_interface interface = { - .version = AST_FRAMEHOOK_INTERFACE_VERSION, - .event_cb = hook_event_cb, - .destroy_cb = hook_destroy_cb, - .data = data, // where the data ptr points to any custom data used later by the hook cb. - }; - int id = ast_framehook_attach(channel, &interface); + struct ast_framehook_interface interface = { + .version = AST_FRAMEHOOK_INTERFACE_VERSION, + .event_cb = hook_event_cb, + .destroy_cb = hook_destroy_cb, + .data = data, // where the data ptr points to any custom data used later by the hook cb. + }; + int id = ast_framehook_attach(channel, &interface); \endcode - The ast_framehook_attach() function creates and attaches a new FrameHook onto - a channel. Once attached to the channel, the FrameHook will call the event_callback - function each time a frame is written or read on the channel. A custom data - pointer can be provided to this function to store on the FrameHook as well. This - pointer can be used to keep up with any statefull information associated with the FrameHook - and is provided during the event_callback function. The destroy_callback function is optional. - This function exists so any custom data stored on the FrameHook can be destroyed before - the Framehook if destroyed. + The ast_framehook_attach() function creates and attaches a new FrameHook onto + a channel. Once attached to the channel, the FrameHook will call the event_callback + function each time a frame is written or read on the channel. A custom data + pointer can be provided to this function to store on the FrameHook as well. This + pointer can be used to keep up with any statefull information associated with the FrameHook + and is provided during the event_callback function. The destroy_callback function is optional. + This function exists so any custom data stored on the FrameHook can be destroyed before + the Framehook if destroyed. \code - ast_framehook_detach(channel, id); + ast_framehook_detach(channel, id); \endcode - The ast_framehook_detach() function signals the FrameHook represented by an id to - be detached and destroyed on a channel. Since it is possible this function may be called - during the FrameHook's event callback, it is impossible to synchronously detach the - FrameHook from the channel during this function call. It is guaranteed that the next - event proceeding the ast_framehook_detach() will be of type AST_FRAMEHOOK_EVENT_DETACH, - and that after that event occurs no other event will ever be issued for that FrameHook. - Once the FrameHook is destroyed, the destroy callback function will be called if it was - provided. Note that if this function is never called, the FrameHook will be detached - on channel destruction. + The ast_framehook_detach() function signals the FrameHook represented by an id to + be detached and destroyed on a channel. Since it is possible this function may be called + during the FrameHook's event callback, it is impossible to synchronously detach the + FrameHook from the channel during this function call. It is guaranteed that the next + event proceeding the ast_framehook_detach() will be of type AST_FRAMEHOOK_EVENT_DETACH, + and that after that event occurs no other event will ever be issued for that FrameHook. + Once the FrameHook is destroyed, the destroy callback function will be called if it was + provided. Note that if this function is never called, the FrameHook will be detached + on channel destruction. \section FrameHookAPICodeExample FrameHook Example Code - The example code below attaches an FrameHook on a channel, and then detachs it when - the first ast_frame is read or written to the event callback function. The Framehook's id - is stored on the FrameHook's data pointer so it can be detached within the callback. + The example code below attaches an FrameHook on a channel, and then detachs it when + the first ast_frame is read or written to the event callback function. The Framehook's id + is stored on the FrameHook's data pointer so it can be detached within the callback. \code - static void destroy_cb(void *data) { - ast_free(data); - } - - static struct ast_frame *event_cb(struct ast_channel *chan, - struct ast_frame *frame, - enum ast_framehook_event event, - void *data) { - - int *id = data; - - if (!frame) { - return frame; - } - - if (event == AST_FRAMEHOOK_EVENT_WRITE) { - ast_log(LOG_NOTICE, "YAY we received a frame in the write direction, Type: %d\n", frame->frametype) - ast_framehook_detach(chan, id); // the channel is guaranteed to be locked during this function call. - } else if (event == AST_FRAMEHOOK_EVENT_READ) { - ast_log(LOG_NOTICE, "YAY we received a frame in the read direction: Type: %d\n", frame->frametype); - ast_framehook_detach(chan, id); // the channel is guaranteed to be locked during this function call. - } - - return frame; - { - - int some_function() - { - struct ast_framehook_interface interface = { - .version = AST_FRAMEHOOK_INTERFACE_VERSION, - .event_cb = hook_event_cb, - .destroy_cb = hook_destroy_cb, - }; - int *id = ast_calloc(1, sizeof(int)); - - if (!id) { - return -1; - } - - interface.data = id; // This data will be returned to us in the callbacks. - - ast_channel_lock(chan); - *id = ast_framehook_attach(chan, &interface); - ast_channel_unlock(chan); - - if (*id < 0) { - // framehook attach failed, free data - ast_free(id); - return -1; - } - return 0; - } + static void destroy_cb(void *data) { + ast_free(data); + } + + static struct ast_frame *event_cb(struct ast_channel *chan, + struct ast_frame *frame, + enum ast_framehook_event event, + void *data) { + + int *id = data; + + if (!frame) { + return frame; + } + + if (event == AST_FRAMEHOOK_EVENT_WRITE) { + ast_log(LOG_NOTICE, "YAY we received a frame in the write direction, Type: %d\n", frame->frametype) + ast_framehook_detach(chan, id); // the channel is guaranteed to be locked during this function call. + } else if (event == AST_FRAMEHOOK_EVENT_READ) { + ast_log(LOG_NOTICE, "YAY we received a frame in the read direction: Type: %d\n", frame->frametype); + ast_framehook_detach(chan, id); // the channel is guaranteed to be locked during this function call. + } + + return frame; + { + + int some_function() + { + struct ast_framehook_interface interface = { + .version = AST_FRAMEHOOK_INTERFACE_VERSION, + .event_cb = hook_event_cb, + .destroy_cb = hook_destroy_cb, + }; + int *id = ast_calloc(1, sizeof(int)); + + if (!id) { + return -1; + } + + interface.data = id; // This data will be returned to us in the callbacks. + + ast_channel_lock(chan); + *id = ast_framehook_attach(chan, &interface); + ast_channel_unlock(chan); + + if (*id < 0) { + // framehook attach failed, free data + ast_free(id); + return -1; + } + return 0; + } \endcode */ @@ -199,7 +199,20 @@ typedef struct ast_frame *(*ast_framehook_event_callback)( */ typedef void (*ast_framehook_destroy_callback)(void *data); -#define AST_FRAMEHOOK_INTERFACE_VERSION 1 +/*! + * \brief This callback is called to determine if the framehook is currently consuming + * frames of a given type + * \since 12 + * + * \param data, The data pointer provided at framehook initilization. + * \param type, The type of frame. + * + * \return 0 if frame type is being ignored + * \return 1 if frame type is not being ignored + */ +typedef int (*ast_framehook_consume_callback)(void *data, enum ast_frame_type type); + +#define AST_FRAMEHOOK_INTERFACE_VERSION 2 /*! This interface is required for attaching a framehook to a channel. */ struct ast_framehook_interface { /*! framehook interface version number */ @@ -209,6 +222,10 @@ struct ast_framehook_interface { /*! destroy_cb is optional. This function is called immediately before the framehook * is destroyed to allow for stored_data cleanup. */ ast_framehook_destroy_callback destroy_cb; + /*! consume_cb is optional. This function is called to query whether the framehook is consuming + * frames of a specific type at this time. If this callback is not implemented it is assumed that the + * framehook will consume frames of all types. */ + ast_framehook_consume_callback consume_cb; /*! This pointer can represent any custom data to be stored on the !framehook. This * data pointer will be provided during each event callback which allows the framehook * to store any stateful data associated with the application using the hook. */ @@ -323,4 +340,19 @@ int ast_framehook_list_is_empty(struct ast_framehook_list *framehooks); */ int ast_framehook_list_contains_no_active(struct ast_framehook_list *framehooks); +/*! + * \brief Determine if a framehook list is free of active framehooks consuming a specific type of frame + * \since 12.3.0 + * \pre The channel must be locked during this function call. + * + * \param framehooks the framehook list + * \retval 0, not empty + * \retval 1, is empty (aside from dying framehooks) + * + * \note This function is very similar to ast_framehook_list_is_empty, but it checks individual + * framehooks to see if they have been marked for destruction and doesn't count them if they are. + */ +int ast_framehook_list_contains_no_active_of_type(struct ast_framehook_list *framehooks, + enum ast_frame_type type); + #endif /* _AST_FRAMEHOOK_H */ diff --git a/main/bridge_basic.c b/main/bridge_basic.c index 9a49b427b..a5827f2ad 100644 --- a/main/bridge_basic.c +++ b/main/bridge_basic.c @@ -2614,6 +2614,12 @@ static struct ast_frame *transfer_target_framehook_cb(struct ast_channel *chan, return frame; } +/*! \brief Callback function which informs upstream if we are consuming a frame of a specific type */ +static int transfer_target_framehook_consume(void *data, enum ast_frame_type type) +{ + return (type == AST_FRAME_CONTROL ? 1 : 0); +} + static void transfer_target_framehook_destroy_cb(void *data) { struct attended_transfer_properties *props = data; @@ -2847,6 +2853,7 @@ static int attach_framehook(struct attended_transfer_properties *props, struct a .version = AST_FRAMEHOOK_INTERFACE_VERSION, .event_cb = transfer_target_framehook_cb, .destroy_cb = transfer_target_framehook_destroy_cb, + .consume_cb = transfer_target_framehook_consume, }; ao2_ref(props, +1); diff --git a/main/channel.c b/main/channel.c index 1161fb676..2d84c771d 100644 --- a/main/channel.c +++ b/main/channel.c @@ -2668,6 +2668,13 @@ int ast_channel_has_audio_frame_or_monitor(struct ast_channel *chan) || !ast_framehook_list_contains_no_active(ast_channel_framehooks(chan)); } +int ast_channel_has_hook_requiring_audio(struct ast_channel *chan) +{ + return ast_channel_monitor(chan) + || !ast_audiohook_write_list_empty(ast_channel_audiohooks(chan)) + || !ast_framehook_list_contains_no_active_of_type(ast_channel_framehooks(chan), AST_FRAME_VOICE); +} + static void destroy_hooks(struct ast_channel *chan) { if (ast_channel_audiohooks(chan)) { diff --git a/main/framehook.c b/main/framehook.c index 0d353cf36..de8c612aa 100644 --- a/main/framehook.c +++ b/main/framehook.c @@ -160,6 +160,10 @@ int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interfac ast_frfree(frame); } + if (ast_channel_is_bridged(chan)) { + ast_softhangup_nolock(chan, AST_SOFTHANGUP_UNBRIDGE); + } + return framehook->id; } @@ -214,6 +218,12 @@ int ast_framehook_list_is_empty(struct ast_framehook_list *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; @@ -229,6 +239,9 @@ int ast_framehook_list_contains_no_active(struct ast_framehook_list *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; } -- cgit v1.2.3