app_confbridge: Enable sending events to participants

ConfBridge can now send events to participants via in-dialog MESSAGEs.
All current Confbridge events are supported, such as ConfbridgeJoin,
ConfbridgeLeave, etc.  In addition to those events, a new event
ConfbridgeWelcome has been added that will send a list of all
current participants to a new participant.

For all but the ConfbridgeWelcome event, the JSON message contains
information about the bridge, such as its id and name, and information
about the channel that triggered the event such as channel name,
callerid info, mute status, and the MSID labels for their audio and
video tracks. You can use the labels to correlate callerid and mute
status to specific video elements in a webrtc client.

To control this behavior, the following options have been added to
confbridge.conf:

bridge_profile/enable_events:  This must be enabled on any bridge where
events are desired.

user_profile/send_events:  This must be set for a user profile to send
events.  Different user profiles connected to the same bridge can have
different settings.  This allows admins to get events but not normal
users for instance.

user_profile/echo_events:  In some cases, you might not want the user
triggering the event to get the event sent back to them.  To prevent it,
set this to false.

A change was also made to res_pjsip_sdp_rtp to save the generated msid
to the stream so it can be re-used.  This allows participant A's video
stream to appear as the same label to all other participants.

Change-Id: I26420aa9f101f0b2387dc9e2fd10733197f1318e
This commit is contained in:
George Joseph
2018-05-31 15:22:13 -06:00
parent a6a22debf1
commit e7a7506f9c
7 changed files with 485 additions and 28 deletions

View File

@@ -68,6 +68,23 @@
<configOption name="admin">
<synopsis>Sets if the user is an admin or not</synopsis>
</configOption>
<configOption name="send_events" default="no">
<synopsis>Sets if events are send to the user</synopsis>
<description><para>If events are enabled for this bridge and this option is
set, users will receive events like join, leave, talking, etc. via text
messages. For users accessing the bridge via chan_pjsip, this means
in-dialog MESSAGE messages. This is most useful for WebRTC participants
where the browser application can use the messages to alter the user
interface.</para></description>
</configOption>
<configOption name="echo_events" default="yes">
<synopsis>Sets if events are echoed back to the user that
triggered them</synopsis>
<description><para>If events are enabled for this user and this option
is set, the user will receive events they trigger, talking, mute, etc.
If not set, they will not receive their own events.
</para></description>
</configOption>
<configOption name="marked">
<synopsis>Sets if this is a marked user or not</synopsis>
</configOption>
@@ -491,6 +508,17 @@
</enumlist>
</description>
</configOption>
<configOption name="enable_events" default="no">
<synopsis>Enables events for this bridge</synopsis>
<description><para>
If enabled, recipients who joined the bridge via a channel driver
that supports Enhanced Messaging (currently only chan_pjsip) will
receive in-dialog messages containing a JSON body describing the
event. The Content-Type header will be
<literal>text/x-ast-confbridge-event</literal>.
This feature must also be enabled in user profiles.</para>
</description>
</configOption>
<configOption name="template">
<synopsis>When using the CONFBRIDGE dialplan function, use a bridge profile as a template for creating a new temporary profile</synopsis>
</configOption>
@@ -1478,6 +1506,12 @@ static char *handle_cli_confbridge_show_user_profile(struct ast_cli_entry *e, in
ast_cli(a->fd,"Admin: %s\n",
u_profile.flags & USER_OPT_ADMIN ?
"true" : "false");
ast_cli(a->fd,"Send Events: %s\n",
u_profile.flags & USER_OPT_SEND_EVENTS ?
"true" : "false");
ast_cli(a->fd,"Echo Events: %s\n",
u_profile.flags & USER_OPT_ECHO_EVENTS ?
"true" : "false");
ast_cli(a->fd,"Marked User: %s\n",
u_profile.flags & USER_OPT_MARKEDUSER ?
"true" : "false");
@@ -1718,6 +1752,10 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e,
break;
}
ast_cli(a->fd,"Enable Events: %s\n",
b_profile.flags & BRIDGE_OPT_ENABLE_EVENTS ?
"yes" : "no");
ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
ast_cli(a->fd,"sound_only_one: %s\n", conf_get_sound(CONF_SOUND_ONLY_ONE, b_profile.sounds));
ast_cli(a->fd,"sound_has_joined: %s\n", conf_get_sound(CONF_SOUND_HAS_JOINED, b_profile.sounds));
@@ -2265,6 +2303,8 @@ int conf_load_config(void)
/* User options */
aco_option_register(&cfg_info, "type", ACO_EXACT, user_types, NULL, OPT_NOOP_T, 0, 0);
aco_option_register(&cfg_info, "admin", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ADMIN);
aco_option_register(&cfg_info, "send_events", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_SEND_EVENTS);
aco_option_register(&cfg_info, "echo_events", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_ECHO_EVENTS);
aco_option_register(&cfg_info, "marked", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MARKEDUSER);
aco_option_register(&cfg_info, "startmuted", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_STARTMUTED);
aco_option_register(&cfg_info, "music_on_hold_when_empty", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_MUSICONHOLD);
@@ -2288,6 +2328,7 @@ int conf_load_config(void)
aco_option_register(&cfg_info, "dsp_talking_threshold", ACO_EXACT, user_types, __stringify(DEFAULT_TALKING_THRESHOLD), OPT_UINT_T, 0, FLDSET(struct user_profile, talking_threshold));
aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, user_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct user_profile, flags), USER_OPT_JITTERBUFFER);
aco_option_register(&cfg_info, "timeout", ACO_EXACT, user_types, "0", OPT_UINT_T, 0, FLDSET(struct user_profile, timeout));
/* This option should only be used with the CONFBRIDGE dialplan function */
aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
@@ -2313,6 +2354,7 @@ int conf_load_config(void)
aco_option_register(&cfg_info, "video_update_discard", ACO_EXACT, bridge_types, "2000", OPT_UINT_T, 0, FLDSET(struct bridge_profile, video_update_discard));
aco_option_register(&cfg_info, "remb_send_interval", ACO_EXACT, bridge_types, "0", OPT_UINT_T, 0, FLDSET(struct bridge_profile, remb_send_interval));
aco_option_register_custom(&cfg_info, "remb_behavior", ACO_EXACT, bridge_types, "average", remb_behavior_handler, 0);
aco_option_register(&cfg_info, "enable_events", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), BRIDGE_OPT_ENABLE_EVENTS);
/* This option should only be used with the CONFBRIDGE dialplan function */
aco_option_register_custom(&cfg_info, "template", ACO_EXACT, bridge_types, NULL, bridge_template_handler, 0);

View File

@@ -227,12 +227,56 @@
static struct stasis_message_router *bridge_state_router;
static struct stasis_message_router *channel_state_router;
STASIS_MESSAGE_TYPE_DEFN(confbridge_start_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_end_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_join_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_leave_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_start_record_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_stop_record_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_mute_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_unmute_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_talking_type);
/*
* The welcome message is defined here but is only sent
* to participants and only when events are enabled.
* At the current time, no actual stasis or AMI events
* are generated for this type.
*/
STASIS_MESSAGE_TYPE_DEFN(confbridge_welcome_type);
const char *confbridge_event_type_to_string(struct stasis_message_type *event_type)
{
if (event_type == confbridge_start_type()) {
return "ConfbridgeStart";
} else if (event_type == confbridge_end_type()) {
return "ConfbridgeEnd";
} else if (event_type == confbridge_join_type()) {
return "ConfbridgeJoin";
} else if (event_type == confbridge_leave_type()) {
return "ConfbridgeLeave";
} else if (event_type == confbridge_start_record_type()) {
return "ConfbridgeRecord";
} else if (event_type == confbridge_stop_record_type()) {
return "ConfbridgeStopRecord";
} else if (event_type == confbridge_mute_type()) {
return "ConfbridgeMute";
} else if (event_type == confbridge_unmute_type()) {
return "ConfbridgeUnmute";
} else if (event_type == confbridge_talking_type()) {
return "ConfbridgeTalking";
} else if (event_type == confbridge_welcome_type()) {
return "ConfbridgeWelcome";
} else {
return "unknown";
}
}
static void confbridge_publish_manager_event(
struct stasis_message *message,
const char *event,
struct ast_str *extra_text)
{
struct ast_bridge_blob *blob = stasis_message_data(message);
const char *event = confbridge_event_type_to_string(stasis_message_type(message));
const char *conference_name;
RAII_VAR(struct ast_str *, bridge_text, NULL, ast_free);
RAII_VAR(struct ast_str *, channel_text, NULL, ast_free);
@@ -291,13 +335,13 @@ static int get_muted_header(struct ast_str **extra_text, struct stasis_message *
static void confbridge_start_cb(void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
confbridge_publish_manager_event(message, "ConfbridgeStart", NULL);
confbridge_publish_manager_event(message, NULL);
}
static void confbridge_end_cb(void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
confbridge_publish_manager_event(message, "ConfbridgeEnd", NULL);
confbridge_publish_manager_event(message, NULL);
}
static void confbridge_leave_cb(void *data, struct stasis_subscription *sub,
@@ -306,7 +350,7 @@ static void confbridge_leave_cb(void *data, struct stasis_subscription *sub,
struct ast_str *extra_text = NULL;
if (!get_admin_header(&extra_text, message)) {
confbridge_publish_manager_event(message, "ConfbridgeLeave", extra_text);
confbridge_publish_manager_event(message, extra_text);
}
ast_free(extra_text);
}
@@ -318,7 +362,7 @@ static void confbridge_join_cb(void *data, struct stasis_subscription *sub,
if (!get_admin_header(&extra_text, message)
&& !get_muted_header(&extra_text, message)) {
confbridge_publish_manager_event(message, "ConfbridgeJoin", extra_text);
confbridge_publish_manager_event(message, extra_text);
}
ast_free(extra_text);
}
@@ -326,13 +370,13 @@ static void confbridge_join_cb(void *data, struct stasis_subscription *sub,
static void confbridge_start_record_cb(void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
confbridge_publish_manager_event(message, "ConfbridgeRecord", NULL);
confbridge_publish_manager_event(message, NULL);
}
static void confbridge_stop_record_cb(void *data, struct stasis_subscription *sub,
struct stasis_message *message)
{
confbridge_publish_manager_event(message, "ConfbridgeStopRecord", NULL);
confbridge_publish_manager_event(message, NULL);
}
static void confbridge_mute_cb(void *data, struct stasis_subscription *sub,
@@ -341,7 +385,7 @@ static void confbridge_mute_cb(void *data, struct stasis_subscription *sub,
struct ast_str *extra_text = NULL;
if (!get_admin_header(&extra_text, message)) {
confbridge_publish_manager_event(message, "ConfbridgeMute", extra_text);
confbridge_publish_manager_event(message, extra_text);
}
ast_free(extra_text);
}
@@ -352,7 +396,7 @@ static void confbridge_unmute_cb(void *data, struct stasis_subscription *sub,
struct ast_str *extra_text = NULL;
if (!get_admin_header(&extra_text, message)) {
confbridge_publish_manager_event(message, "ConfbridgeUnmute", extra_text);
confbridge_publish_manager_event(message, extra_text);
}
ast_free(extra_text);
}
@@ -373,20 +417,10 @@ static void confbridge_talking_cb(void *data, struct stasis_subscription *sub,
}
if (!get_admin_header(&extra_text, message)) {
confbridge_publish_manager_event(message, "ConfbridgeTalking", extra_text);
confbridge_publish_manager_event(message, extra_text);
}
}
STASIS_MESSAGE_TYPE_DEFN(confbridge_start_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_end_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_join_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_leave_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_start_record_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_stop_record_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_mute_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_unmute_type);
STASIS_MESSAGE_TYPE_DEFN(confbridge_talking_type);
void manager_confbridge_shutdown(void) {
STASIS_MESSAGE_TYPE_CLEANUP(confbridge_start_type);
STASIS_MESSAGE_TYPE_CLEANUP(confbridge_end_type);
@@ -397,6 +431,7 @@ void manager_confbridge_shutdown(void) {
STASIS_MESSAGE_TYPE_CLEANUP(confbridge_mute_type);
STASIS_MESSAGE_TYPE_CLEANUP(confbridge_unmute_type);
STASIS_MESSAGE_TYPE_CLEANUP(confbridge_talking_type);
STASIS_MESSAGE_TYPE_CLEANUP(confbridge_welcome_type);
if (bridge_state_router) {
stasis_message_router_unsubscribe(bridge_state_router);
@@ -420,6 +455,7 @@ int manager_confbridge_init(void)
STASIS_MESSAGE_TYPE_INIT(confbridge_mute_type);
STASIS_MESSAGE_TYPE_INIT(confbridge_unmute_type);
STASIS_MESSAGE_TYPE_INIT(confbridge_talking_type);
STASIS_MESSAGE_TYPE_INIT(confbridge_welcome_type);
bridge_state_router = stasis_message_router_create(
ast_bridge_topic_all_cached());
@@ -564,5 +600,7 @@ int manager_confbridge_init(void)
return -1;
}
/* FYI: confbridge_welcome_type is never routed */
return 0;
}

View File

@@ -65,6 +65,8 @@ enum user_profile_flags {
USER_OPT_ANNOUNCEUSERCOUNTALL = (1 << 14), /*!< Sets if the number of users should be announced to everyone. */
USER_OPT_JITTERBUFFER = (1 << 15), /*!< Places a jitterbuffer on the user. */
USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW = (1 << 16), /*!< modifies ANNOUNCE_JOIN_LEAVE - user reviews the recording before continuing */
USER_OPT_SEND_EVENTS = (1 << 17), /*!< Send text message events to users */
USER_OPT_ECHO_EVENTS = (1 << 18), /*!< Send events only to the admin(s) */
};
enum bridge_profile_flags {
@@ -79,6 +81,7 @@ enum bridge_profile_flags {
BRIDGE_OPT_REMB_BEHAVIOR_AVERAGE = (1 << 8), /*!< The average of all REMB reports is sent to the sender */
BRIDGE_OPT_REMB_BEHAVIOR_LOWEST = (1 << 9), /*!< The lowest estimated maximum bitrate is sent to the sender */
BRIDGE_OPT_REMB_BEHAVIOR_HIGHEST = (1 << 10), /*!< The highest estimated maximum bitrate is sent to the sender */
BRIDGE_OPT_ENABLE_EVENTS = (1 << 11), /*!< Enable sending events to participants */
};
enum conf_menu_action_id {
@@ -625,6 +628,26 @@ struct stasis_message_type *confbridge_unmute_type(void);
*/
struct stasis_message_type *confbridge_talking_type(void);
/*!
* \since 15.5
* \brief get the confbridge welcome stasis message type
*
* \retval stasis message type for confbridge welcome messages if it's available
* \retval NULL if it isn't
*/
struct stasis_message_type *confbridge_welcome_type(void);
/*!
* \since 15.5
* \brief Get the string representation of a confbridge stasis message type
*
* \param event_type The confbridge event type such as 'confbridge_welcome_type()'
*
* \retval The string representation of the message type
* \retval "unknown" if not found
*/
const char *confbridge_event_type_to_string(struct stasis_message_type *event_type);
/*!
* \since 12.0
* \brief register stasis message routers to handle manager events for confbridge messages