@ -71,6 +71,8 @@
# include "asterisk/json.h"
# include "asterisk/format_cache.h"
# include "asterisk/taskprocessor.h"
# include "asterisk/stream.h"
# include "asterisk/message.h"
/*** DOCUMENTATION
< application name = " ConfBridge " language = " en_US " >
@ -547,6 +549,315 @@ const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds
return " " ;
}
static struct ast_json * channel_to_json ( struct ast_channel_snapshot * channel_snapshot ,
struct ast_json * conf_blob , struct ast_json * labels_blob )
{
struct ast_json * json_channel = ast_channel_snapshot_to_json ( channel_snapshot , NULL ) ;
if ( ! json_channel ) {
return NULL ;
}
/* These items are removed for privacy reasons. */
ast_json_object_del ( json_channel , " dialplan " ) ;
ast_json_object_del ( json_channel , " connected " ) ;
ast_json_object_del ( json_channel , " accountcode " ) ;
/* conf_blob contains flags such as talking, admin, mute, etc. */
if ( conf_blob ) {
struct ast_json * conf_copy = ast_json_copy ( conf_blob ) ;
if ( ! conf_copy ) {
ast_json_unref ( json_channel ) ;
return NULL ;
}
ast_json_object_del ( conf_copy , " conference " ) ;
ast_json_object_update ( json_channel , conf_copy ) ;
ast_json_unref ( conf_copy ) ;
}
/* labels_blob contains the msid labels to correlate to streams. */
if ( labels_blob ) {
ast_json_object_update ( json_channel , labels_blob ) ;
}
return json_channel ;
}
static struct ast_json * bridge_to_json ( struct ast_bridge_snapshot * bridge_snapshot )
{
struct ast_json * json_bridge = ast_bridge_snapshot_to_json ( bridge_snapshot , NULL ) ;
if ( ! json_bridge ) {
return NULL ;
}
/* These items have no use in the context of bridge participant info. */
ast_json_object_del ( json_bridge , " technology " ) ;
ast_json_object_del ( json_bridge , " bridge_type " ) ;
ast_json_object_del ( json_bridge , " bridge_class " ) ;
ast_json_object_del ( json_bridge , " creator " ) ;
ast_json_object_del ( json_bridge , " channels " ) ;
return json_bridge ;
}
static struct ast_json * pack_bridge_and_channels (
struct ast_json * json_bridge , struct ast_json * json_channels ,
struct stasis_message * msg )
{
const struct timeval * tv = stasis_message_timestamp ( msg ) ;
const char * msg_name = confbridge_event_type_to_string ( stasis_message_type ( msg ) ) ;
const char * fmt = ast_json_typeof ( json_channels ) = = AST_JSON_ARRAY ?
" {s: s, s: o, s: o, s: o } " : " {s: s, s: o, s: o, s: [ o ] } " ;
return ast_json_pack ( fmt ,
" type " , msg_name ,
" timestamp " , ast_json_timeval ( * tv , NULL ) ,
" bridge " , json_bridge ,
" channels " , json_channels ) ;
}
static struct ast_json * pack_snapshots ( struct ast_bridge_snapshot * bridge_snapshot ,
struct ast_channel_snapshot * channel_snapshot , struct ast_json * conf_blob ,
struct ast_json * labels_blob , struct stasis_message * msg )
{
struct ast_json * json_bridge ;
struct ast_json * json_channel ;
json_bridge = bridge_to_json ( bridge_snapshot ) ;
json_channel = channel_to_json ( channel_snapshot , conf_blob , labels_blob ) ;
return pack_bridge_and_channels ( json_bridge , json_channel , msg ) ;
}
enum label_direction {
LABEL_DIRECTION_SRC ,
LABEL_DIRECTION_DEST ,
} ;
static struct ast_stream * get_stream ( struct ast_stream_topology * topology ,
enum ast_media_type m_type )
{
int count ;
int i ;
count = ast_stream_topology_get_count ( topology ) ;
if ( count < 0 ) {
return NULL ;
}
for ( i = 0 ; i < count ; i + + ) {
struct ast_stream * s ;
enum ast_stream_state s_state ;
enum ast_media_type s_type ;
s = ast_stream_topology_get_stream ( topology , i ) ;
s_state = ast_stream_get_state ( s ) ;
s_type = ast_stream_get_type ( s ) ;
if ( s_type = = m_type
& & ( s_state = = AST_STREAM_STATE_SENDRECV | | s_state = = AST_STREAM_STATE_RECVONLY ) ) {
return s ;
}
}
return NULL ;
}
static struct ast_json * get_media_labels ( struct confbridge_conference * conference ,
struct ast_channel * src_chan , struct ast_channel * dest_chan , enum label_direction dir )
{
struct ast_stream_topology * topology ;
struct ast_stream * stream ;
const char * curr_a_label ;
const char * a_label = NULL ;
const char * v_label = NULL ;
struct ast_json * labels = ast_json_array_create ( ) ;
if ( ! labels ) {
return NULL ;
}
topology = ast_channel_get_stream_topology ( dir = = LABEL_DIRECTION_SRC ? src_chan : dest_chan ) ;
stream = get_stream ( topology , AST_MEDIA_TYPE_AUDIO ) ;
curr_a_label = stream ? ast_stream_get_metadata ( stream , " MSID:LABEL " ) : NULL ;
a_label = curr_a_label ? : conference - > bridge - > uniqueid ;
ast_json_array_append ( labels , ast_json_string_create ( a_label ) ) ;
topology = ast_channel_get_stream_topology ( dir = = LABEL_DIRECTION_SRC ? dest_chan : src_chan ) ;
stream = get_stream ( topology , AST_MEDIA_TYPE_VIDEO ) ;
v_label = stream ? ast_stream_get_metadata ( stream , " MSID:LABEL " ) : NULL ;
if ( v_label ) {
ast_json_array_append ( labels , ast_json_string_create ( v_label ) ) ;
}
return ast_json_pack ( " {s: o } " , " media_source_track_labels " , labels ) ;
}
static void send_message ( const char * msg_name , char * conf_name , struct ast_json * json_object ,
struct ast_channel * chan )
{
struct ast_msg_data * data_msg ;
struct ast_msg_data_attribute attrs [ ] = {
{ . type = AST_MSG_DATA_ATTR_FROM , conf_name } ,
{ . type = AST_MSG_DATA_ATTR_CONTENT_TYPE , . value = " application/x-asterisk-confbridge-event+json " } ,
{ . type = AST_MSG_DATA_ATTR_BODY , } ,
} ;
char * json ;
int rc = 0 ;
json = ast_json_dump_string_format ( json_object , AST_JSON_PRETTY ) ;
if ( ! json ) {
ast_log ( LOG_ERROR , " Unable to convert json_object for %s message to string \n " , msg_name ) ;
return ;
}
attrs [ 2 ] . value = json ;
data_msg = ast_msg_data_alloc ( AST_MSG_DATA_SOURCE_TYPE_IN_DIALOG , attrs , ARRAY_LEN ( attrs ) ) ;
if ( ! data_msg ) {
ast_log ( LOG_ERROR , " Unable to create %s message for channel '%s' \n " , msg_name ,
ast_channel_name ( chan ) ) ;
ast_json_free ( json ) ;
return ;
}
rc = ast_sendtext_data ( chan , data_msg ) ;
ast_free ( data_msg ) ;
if ( rc ! = 0 ) {
/* Don't complain if we can't send a leave message. The channel is probably gone. */
if ( strcmp ( confbridge_event_type_to_string ( confbridge_leave_type ( ) ) , msg_name ) ! = 0 ) {
ast_log ( LOG_ERROR , " Failed to queue %s message to '%s' \n %s \n " , msg_name ,
ast_channel_name ( chan ) , json ) ;
}
ast_json_free ( json ) ;
return ;
}
ast_debug ( 3 , " Queued %s message to '%s' \n %s \n " , msg_name , ast_channel_name ( chan ) , json ) ;
ast_json_free ( json ) ;
}
static void send_event_to_participants ( struct confbridge_conference * conference ,
struct ast_channel * chan , struct stasis_message * msg )
{
struct ast_bridge_blob * obj = stasis_message_data ( msg ) ;
struct ast_json * extras = obj - > blob ;
struct user_profile u_profile = { { 0 } } ;
int source_send_events = 0 ;
int source_echo_events = 0 ;
struct ast_json * json_channels = NULL ;
struct confbridge_user * user ;
const char * msg_name = confbridge_event_type_to_string ( stasis_message_type ( msg ) ) ;
ast_debug ( 3 , " Distributing %s event to participants \n " , msg_name ) ;
/* This could be a channel level event or a bridge level event */
if ( chan ) {
if ( ! conf_find_user_profile ( chan , NULL , & u_profile ) ) {
ast_log ( LOG_ERROR , " Unable to retrieve user profile for channel '%s' \n " ,
ast_channel_name ( chan ) ) ;
return ;
}
source_send_events = ast_test_flag ( & u_profile , USER_OPT_SEND_EVENTS ) ;
source_echo_events = ast_test_flag ( & u_profile , USER_OPT_ECHO_EVENTS ) ;
ast_debug ( 3 , " send_events: %d echo_events: %d for profile %s \n " ,
source_send_events , source_echo_events , u_profile . name ) ;
}
/* Now send a message to the participants with the json string. */
ao2_lock ( conference ) ;
AST_LIST_TRAVERSE ( & conference - > active_list , user , list ) {
struct ast_json * json_object ;
struct ast_json * source_json_labels = NULL ;
/*
* If the msg type is join , we need to capture all targets channel info so we can
* send a welcome message to the source channel with all current participants .
*/
if ( source_send_events & & stasis_message_type ( msg ) = = confbridge_join_type ( ) ) {
struct ast_channel_snapshot * target_snapshot ;
struct ast_json * target_json_channel ;
struct ast_json * target_json_labels ;
target_snapshot = ast_channel_snapshot_get_latest ( ast_channel_uniqueid ( user - > chan ) ) ;
if ( ! target_snapshot ) {
ast_log ( LOG_ERROR , " Unable to get a channel snapshot for '%s' \n " ,
ast_channel_name ( user - > chan ) ) ;
continue ;
}
target_json_labels = get_media_labels ( conference , chan , user - > chan , LABEL_DIRECTION_SRC ) ;
target_json_channel = channel_to_json ( target_snapshot , extras , target_json_labels ) ;
ao2_ref ( target_snapshot , - 1 ) ;
ast_json_unref ( target_json_labels ) ;
if ( ! json_channels ) {
json_channels = ast_json_array_create ( ) ;
if ( ! json_channels ) {
ast_log ( LOG_ERROR , " Unable to allocate json array \n " ) ;
ast_json_unref ( target_json_channel ) ;
ast_json_unref ( target_json_labels ) ;
return ;
}
}
ast_json_array_append ( json_channels , target_json_channel ) ;
}
/* Don't send a message to the user that triggered the event. */
if ( ! source_echo_events & & user - > chan = = chan ) {
ast_debug ( 3 , " Skipping queueing %s message to '%s'. Same channel. \n " , msg_name ,
ast_channel_name ( user - > chan ) ) ;
continue ;
}
/* Don't send a message to users in profiles not sending events. */
if ( ! ast_test_flag ( & user - > u_profile , USER_OPT_SEND_EVENTS ) ) {
ast_debug ( 3 , " Skipping queueing %s message to '%s'. Not receiving events. \n " , msg_name ,
ast_channel_name ( user - > chan ) ) ;
continue ;
}
source_json_labels = get_media_labels ( conference , chan , user - > chan , LABEL_DIRECTION_DEST ) ;
ast_json_object_update ( extras , source_json_labels ) ;
json_object = pack_snapshots ( obj - > bridge , obj - > channel , extras , source_json_labels , msg ) ;
ast_json_unref ( source_json_labels ) ;
if ( ! json_object ) {
ast_log ( LOG_ERROR , " Unable to convert %s message to json \n " , msg_name ) ;
continue ;
}
send_message ( msg_name , conference - > name , json_object , user - > chan ) ;
ast_json_unref ( json_object ) ;
}
ao2_unlock ( conference ) ;
/*
* If this is a join event , send the welcome message to just the joining user
* if it ' s not audio - only or otherwise restricted .
*/
if ( source_send_events & & json_channels
& & stasis_message_type ( msg ) = = confbridge_join_type ( ) ) {
struct ast_json * json_object ;
struct ast_json * json_bridge ;
const char * welcome_msg_name = confbridge_event_type_to_string ( confbridge_welcome_type ( ) ) ;
json_bridge = bridge_to_json ( obj - > bridge ) ;
json_object = pack_bridge_and_channels ( json_bridge , json_channels , msg ) ;
if ( ! json_object ) {
ast_log ( LOG_ERROR , " Unable to convert ConfbridgeWelcome message to json \n " ) ;
return ;
}
ast_json_string_set ( ast_json_object_get ( json_object , " type " ) , welcome_msg_name ) ;
send_message ( welcome_msg_name , conference - > name , json_object , chan ) ;
ast_json_unref ( json_object ) ;
}
}
static void send_conf_stasis ( struct confbridge_conference * conference , struct ast_channel * chan ,
struct stasis_message_type * type , struct ast_json * extras , int channel_topic )
{
@ -573,6 +884,10 @@ static void send_conf_stasis(struct confbridge_conference *conference, struct as
return ;
}
if ( ast_test_flag ( & conference - > b_profile , BRIDGE_OPT_ENABLE_EVENTS ) ) {
send_event_to_participants ( conference , chan , msg ) ;
}
if ( channel_topic ) {
stasis_publish ( ast_channel_topic ( chan ) , msg ) ;
} else {