Commit c2ee154ed2 for asterisk.org
commit c2ee154ed2897a8461fd2f39733a4b4f4c627bd1
Author: George Joseph <gjoseph@sangoma.com>
Date: Fri Mar 6 11:52:53 2026 -0700
rtp: Set RTPAUDIOQOS variables when ast_softhangup is called.
If a channel in Stasis/ARI is hung up by the channel driver, the RTPAUDIOQOS
variables are set before the channel leaves Stasis and are therefore
available to the ARI app via ChannelVarset events. If the channel is hung up
by ARI however, the channel leaves Stasis before the RTPAUDIOQOS variables
are set so the app may not get the ChannelVarset events.
We now set the RTPAUDIOQOS variables when ast_softhangup() is called as well
as when the channel driver hangs up a channel. Since ARI hangups call
ast_softhangup(), the variables will be set before the channel leaves Stasis
and the app should get the ChannelVarset events.
ast_rtp_instance_set_stats_vars(), which actually sets the variables, now
checks to see if the variables are already set before attempting to set them.
This prevents double messages from being generated.
Resolves: #1816
diff --git a/main/channel.c b/main/channel.c
index 76e7c3c787..ff2e38a343 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -75,6 +75,7 @@
#include "asterisk/max_forwards.h"
#include "asterisk/stream.h"
#include "asterisk/message.h"
+#include "asterisk/rtp_engine.h"
#include "channelstorage.h"
@@ -2463,8 +2464,21 @@ int ast_softhangup(struct ast_channel *chan, int cause)
RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
int res;
int tech_cause = 0;
+ struct ast_rtp_glue *glue;
+ RAII_VAR(struct ast_rtp_instance *, rtp, NULL, ao2_cleanup);
+ const struct ast_channel_tech *tech;
ast_channel_lock(chan);
+
+ tech = ast_channel_tech(chan);
+ glue = ast_rtp_instance_get_glue(tech->type);
+ if (glue) {
+ glue->get_rtp_info(chan, &rtp);
+ if (rtp) {
+ ast_rtp_instance_set_stats_vars(chan, rtp);
+ }
+ }
+
res = ast_softhangup_nolock(chan, cause);
blob = ast_json_pack("{s: i, s: b}",
"cause", cause,
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 90198e089d..b0c1bc2c42 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -2704,14 +2704,36 @@ char *ast_rtp_instance_get_quality(struct ast_rtp_instance *instance, enum ast_r
return buf;
}
+#define SET_STATS_VAR_HELPER(var_prefix, field) \
+({ \
+ value = ast_rtp_instance_get_quality(instance, field, quality_buf, sizeof(quality_buf)); \
+ if (value) { \
+ if (!chanvars || ast_strlen_zero(ast_var_find(chanvars, var_prefix))) { \
+ pbx_builtin_setvar_helper(chan, var_prefix, value); \
+ chanchanges++; \
+ } \
+ if (bridge) { \
+ if (!bridgevars || ast_strlen_zero(ast_var_find(bridgevars, var_prefix "BRIDGED"))) { \
+ pbx_builtin_setvar_helper(bridge, var_prefix "BRIDGED", value); \
+ bridgechanges++; \
+ } \
+ } \
+ } \
+})
+
void ast_rtp_instance_set_stats_vars(struct ast_channel *chan, struct ast_rtp_instance *instance)
{
char quality_buf[AST_MAX_USER_FIELD];
- char *quality;
+ char *value;
struct ast_channel *bridge;
+ struct varshead *chanvars = ast_channel_varshead(chan);
+ struct varshead *bridgevars = NULL;
+ int chanchanges = 0;
+ int bridgechanges = 0;
bridge = ast_channel_bridge_peer(chan);
if (bridge) {
+ bridgevars = ast_channel_varshead(bridge);
ast_channel_lock_both(chan, bridge);
ast_channel_stage_snapshot(bridge);
} else {
@@ -2719,55 +2741,28 @@ void ast_rtp_instance_set_stats_vars(struct ast_channel *chan, struct ast_rtp_in
}
ast_channel_stage_snapshot(chan);
- quality = ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY,
- quality_buf, sizeof(quality_buf));
- if (quality) {
- pbx_builtin_setvar_helper(chan, "RTPAUDIOQOS", quality);
- if (bridge) {
- pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSBRIDGED", quality);
- }
- }
+ SET_STATS_VAR_HELPER("RTPAUDIOQOS", AST_RTP_INSTANCE_STAT_FIELD_QUALITY);
- quality = ast_rtp_instance_get_quality(instance,
- AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER, quality_buf, sizeof(quality_buf));
- if (quality) {
- pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSJITTER", quality);
- if (bridge) {
- pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSJITTERBRIDGED", quality);
- }
- }
+ SET_STATS_VAR_HELPER("RTPAUDIOQOSJITTER", AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER);
- quality = ast_rtp_instance_get_quality(instance,
- AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS, quality_buf, sizeof(quality_buf));
- if (quality) {
- pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSLOSS", quality);
- if (bridge) {
- pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSLOSSBRIDGED", quality);
- }
- }
+ SET_STATS_VAR_HELPER("RTPAUDIOQOSLOSS", AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS);
- quality = ast_rtp_instance_get_quality(instance,
- AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT, quality_buf, sizeof(quality_buf));
- if (quality) {
- pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSRTT", quality);
- if (bridge) {
- pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSRTTBRIDGED", quality);
- }
- }
+ SET_STATS_VAR_HELPER("RTPAUDIOQOSRTT", AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT);
- quality = ast_rtp_instance_get_quality(instance,
- AST_RTP_INSTANCE_STAT_FIELD_QUALITY_MES, quality_buf, sizeof(quality_buf));
- if (quality) {
- pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSMES", quality);
- if (bridge) {
- pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSMESBRIDGED", quality);
- }
- }
+ SET_STATS_VAR_HELPER("RTPAUDIOQOSMES", AST_RTP_INSTANCE_STAT_FIELD_QUALITY_MES);
- ast_channel_stage_snapshot_done(chan);
+ if (chanchanges) {
+ ast_channel_stage_snapshot_done(chan);
+ } else {
+ ast_clear_flag(ast_channel_flags(chan), AST_FLAG_SNAPSHOT_STAGE);
+ }
ast_channel_unlock(chan);
if (bridge) {
- ast_channel_stage_snapshot_done(bridge);
+ if (bridgechanges) {
+ ast_channel_stage_snapshot_done(bridge);
+ } else {
+ ast_clear_flag(ast_channel_flags(bridge), AST_FLAG_SNAPSHOT_STAGE);
+ }
ast_channel_unlock(bridge);
ast_channel_unref(bridge);
}