Skip to content

Commit caf2656

Browse files
committed
TEL-6872: Cherry-pick JB statistics race condition fix from SignalWire PR signalwire#2337
Consolidate inline channel variable exports from the jitter buffer hot path into switch_jb_export_stats(), called at hangup before hangup hooks run (via switch_core_media_export_jb_stats) and as a safety net before every switch_jb_destroy() in both switch_rtp_destroy() and the KILL_JB path in read_rtp_packet(). This prevents a race condition where hangup hooks read stale/missing JB stats because the jitter buffer was already destroyed or the inline exports hadn't run recently enough. Adapted from upstream commit 0388398 — excluded fast_acceleration, forced_acceleration, and packets_in_buffer fields which don't exist in our switch_jb struct. Cherry-picked from signalwire#2337 (Julien Chavanton).
1 parent fde88ec commit caf2656

7 files changed

Lines changed: 159 additions & 34 deletions

File tree

src/include/switch_core_media.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ SWITCH_DECLARE(void) switch_core_media_resume(switch_core_session_t *session);
352352
SWITCH_DECLARE(void) switch_core_media_init(void);
353353
SWITCH_DECLARE(void) switch_core_media_deinit(void);
354354
SWITCH_DECLARE(void) switch_core_media_set_stats(switch_core_session_t *session);
355+
SWITCH_DECLARE(void) switch_core_media_export_jb_stats(switch_core_session_t *session);
355356
SWITCH_DECLARE(void) switch_core_media_sync_stats(switch_core_session_t *session);
356357
SWITCH_DECLARE(void) switch_core_session_wake_video_thread(switch_core_session_t *session);
357358
SWITCH_DECLARE(void) switch_core_session_clear_crypto(switch_core_session_t *session);

src/include/switch_jitterbuffer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ SWITCH_DECLARE(void) switch_jb_set_flag(switch_jb_t *jb, switch_jb_flag_t flag);
6767
SWITCH_DECLARE(void) switch_jb_clear_flag(switch_jb_t *jb, switch_jb_flag_t flag);
6868
SWITCH_DECLARE(uint32_t) switch_jb_get_nack_success(switch_jb_t *jb);
6969
SWITCH_DECLARE(uint32_t) switch_jb_get_packets_per_frame(switch_jb_t *jb);
70+
SWITCH_DECLARE(void) switch_jb_export_stats(switch_jb_t *jb);
7071

7172
SWITCH_END_EXTERN_C
7273
#endif

src/include/switch_rtp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_debug_jitter_buffer(switch_rtp_t *rtp
552552
SWITCH_DECLARE(switch_status_t) switch_rtp_deactivate_jitter_buffer(switch_rtp_t *rtp_session);
553553
SWITCH_DECLARE(switch_status_t) switch_rtp_pause_jitter_buffer(switch_rtp_t *rtp_session, switch_bool_t pause);
554554
SWITCH_DECLARE(switch_jb_t *) switch_rtp_get_jitter_buffer(switch_rtp_t *rtp_session);
555+
SWITCH_DECLARE(switch_jb_t *) switch_rtp_get_jitter_buffer_for_stats(switch_rtp_t *rtp_session);
555556

556557

557558

src/switch_core_media.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2553,13 +2553,66 @@ SWITCH_DECLARE(void) switch_core_media_sync_stats(switch_core_session_t *session
25532553

25542554
}
25552555

2556+
static void set_jb_stats(switch_core_session_t *session, switch_media_type_t type)
2557+
{
2558+
switch_media_handle_t *smh;
2559+
switch_rtp_engine_t *engine;
2560+
switch_jb_t *jb;
2561+
const char *type_str = (type == SWITCH_MEDIA_TYPE_AUDIO) ? "audio" :
2562+
(type == SWITCH_MEDIA_TYPE_VIDEO) ? "video" : "text";
2563+
2564+
if (!(smh = session->media_handle)) {
2565+
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
2566+
"set_jb_stats(%s): no media handle\n", type_str);
2567+
return;
2568+
}
2569+
2570+
engine = &smh->engines[type];
2571+
if (!engine->rtp_session) {
2572+
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG,
2573+
"set_jb_stats(%s): no rtp_session\n", type_str);
2574+
return;
2575+
}
2576+
2577+
/* Use _for_stats variant which bypasses switch_rtp_ready() check
2578+
* since RTP session may not be "ready" during hangup but JB still exists */
2579+
jb = switch_rtp_get_jitter_buffer_for_stats(engine->rtp_session);
2580+
if (jb) {
2581+
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO,
2582+
"set_jb_stats(%s): exporting JB stats\n", type_str);
2583+
switch_jb_export_stats(jb);
2584+
} else {
2585+
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO,
2586+
"set_jb_stats(%s): no jitter buffer\n", type_str);
2587+
}
2588+
}
2589+
2590+
SWITCH_DECLARE(void) switch_core_media_export_jb_stats(switch_core_session_t *session)
2591+
{
2592+
if (!session->media_handle) {
2593+
return;
2594+
}
2595+
2596+
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO,
2597+
"switch_core_media_export_jb_stats() exporting JB stats before hangup handlers\n");
2598+
2599+
/* Export jitter buffer stats so they're available in hangup hooks */
2600+
set_jb_stats(session, SWITCH_MEDIA_TYPE_AUDIO);
2601+
set_jb_stats(session, SWITCH_MEDIA_TYPE_VIDEO);
2602+
set_jb_stats(session, SWITCH_MEDIA_TYPE_TEXT);
2603+
}
2604+
25562605
SWITCH_DECLARE(void) switch_core_media_set_stats(switch_core_session_t *session)
25572606
{
2607+
switch_channel_t *channel = switch_core_session_get_channel(session);
25582608

25592609
if (!session->media_handle) {
25602610
return;
25612611
}
25622612

2613+
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO,
2614+
"[%s] switch_core_media_set_stats() called\n", switch_channel_get_name(channel));
2615+
25632616
switch_core_media_sync_stats(session);
25642617

25652618
set_stats(session, SWITCH_MEDIA_TYPE_AUDIO, "audio");

src/switch_core_state_machine.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,9 @@ SWITCH_DECLARE(void) switch_core_session_hangup_state(switch_core_session_t *ses
881881
switch_channel_set_timestamps(session->channel);
882882
switch_channel_set_callstate(session->channel, CCS_HANGUP);
883883

884+
/* Export JB stats before hangup handlers run, while JB still exists */
885+
switch_core_media_export_jb_stats(session);
886+
884887
STATE_MACRO(hangup, "HANGUP");
885888

886889
switch_core_media_set_stats(session);

src/switch_jitterbuffer.c

Lines changed: 81 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -983,24 +983,11 @@ static inline int check_jb_size(switch_jb_t *jb)
983983

984984
/* update the stats every x packets */
985985
if (target_seq_hs % 50 == 0) {
986-
int packet_ms = jb->jitter.samples_per_frame / (jb->jitter.samples_per_second / 1000);
987-
988986
jb->jitter.stats.estimate_ms = (*jb->jitter.estimate) / jb->jitter.samples_per_second * 1000;
989-
if (jb->channel) {
990-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_size_max_ms", "%u", jb->jitter.stats.size_max * packet_ms);
991-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_size_est_ms", "%u", jb->jitter.stats.size_est * packet_ms);
992-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_acceleration_ms", "%u", jb->jitter.stats.acceleration * packet_ms);
993-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_expand_ms", "%u", jb->jitter.stats.expand * packet_ms);
994-
}
995987

996988
if (jb->jitter.stats.jitter_max_ms < jb->jitter.stats.estimate_ms) {
997989
jb->jitter.stats.jitter_max_ms = jb->jitter.stats.estimate_ms;
998990
}
999-
1000-
if (jb->channel) {
1001-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_jitter_max_ms", "%u", jb->jitter.stats.jitter_max_ms);
1002-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_jitter_est_ms", "%u", jb->jitter.stats.estimate_ms);
1003-
}
1004991
}
1005992

1006993
if (old) {
@@ -1102,19 +1089,6 @@ SWITCH_DECLARE(void) switch_jb_set_jitter_estimator(switch_jb_t *jb, double *jit
11021089
{
11031090
if (jb && jitter) {
11041091
memset(&jb->jitter, 0, sizeof(switch_jb_jitter_t));
1105-
if (jb->channel) {
1106-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_max_ms", "%u", 0);
1107-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_size_ms", "%u", 0);
1108-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_acceleration_ms", "%u", 0);
1109-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_expand_ms", "%u", 0);
1110-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_jitter_max_ms", "%u", 0);
1111-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_jitter_ms", "%u", 0);
1112-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_count", "%u", 0);
1113-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_too_big", "%u", 0);
1114-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_missing_frames", "%u", 0);
1115-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_ts_jump", "%u", 0);
1116-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_error", "%u", 0);
1117-
}
11181092

11191093
jb->jitter.estimate = jitter;
11201094
jb->jitter.samples_per_frame = samples_per_frame;
@@ -1197,14 +1171,6 @@ SWITCH_DECLARE(void) switch_jb_reset(switch_jb_t *jb)
11971171
{
11981172
jb->jitter.stats.reset++;
11991173
jb->jitter.stats.expand_frame_len = 0;
1200-
if (jb->channel) {
1201-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_count", "%u", jb->jitter.stats.reset);
1202-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_too_big", "%u", jb->jitter.stats.reset_too_big);
1203-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_too_expanded", "%u", jb->jitter.stats.reset_too_expanded);
1204-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_missing_frames", "%u", jb->jitter.stats.reset_missing_frames);
1205-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_ts_jump", "%u", jb->jitter.stats.reset_ts_jump);
1206-
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_error", "%u", jb->jitter.stats.reset_error);
1207-
}
12081174

12091175
if (jb->type == SJB_VIDEO) {
12101176
switch_mutex_lock(jb->mutex);
@@ -1378,6 +1344,87 @@ SWITCH_DECLARE(switch_status_t) switch_jb_create(switch_jb_t **jbp, switch_jb_ty
13781344
return SWITCH_STATUS_SUCCESS;
13791345
}
13801346

1347+
SWITCH_DECLARE(void) switch_jb_export_stats(switch_jb_t *jb)
1348+
{
1349+
int packet_ms = 0;
1350+
1351+
if (!jb || !jb->channel) {
1352+
return;
1353+
}
1354+
1355+
switch_mutex_lock(jb->mutex);
1356+
1357+
switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(jb->channel), SWITCH_LOG_INFO,
1358+
"switch_jb_export_stats: type=%s elastic=%s reset_count=%u\n",
1359+
jb->type == SJB_VIDEO ? "video" : (jb->type == SJB_AUDIO ? "audio" : "text"),
1360+
jb->elastic ? "true" : "false",
1361+
jb->jitter.stats.reset);
1362+
1363+
/* Export jitter buffer configuration */
1364+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_type", "%s",
1365+
jb->type == SJB_VIDEO ? "video" : (jb->type == SJB_AUDIO ? "audio" : "text"));
1366+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_min_frame_len", "%u", jb->min_frame_len);
1367+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_max_frame_len", "%u", jb->max_frame_len);
1368+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_cur_frame_len", "%u", jb->frame_len);
1369+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_highest_frame_len", "%u", jb->highest_frame_len);
1370+
1371+
/* Export buffer state */
1372+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_visible_nodes", "%u", jb->visible_nodes);
1373+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_allocated_nodes", "%u", jb->allocated_nodes);
1374+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_complete_frames", "%u", jb->complete_frames);
1375+
1376+
/* Export miss/hit statistics */
1377+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_period_miss_count", "%u", jb->period_miss_count);
1378+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_consec_miss_count", "%u", jb->consec_miss_count);
1379+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_period_good_count", "%u", jb->period_good_count);
1380+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_consec_good_count", "%u", jb->consec_good_count);
1381+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_period_miss_pct", "%.2f", jb->period_miss_pct);
1382+
1383+
/* Export jitter estimator statistics */
1384+
if (jb->jitter.samples_per_frame && jb->jitter.samples_per_second) {
1385+
packet_ms = jb->jitter.samples_per_frame / (jb->jitter.samples_per_second / 1000);
1386+
}
1387+
1388+
if (jb->jitter.estimate && jb->jitter.samples_per_second) {
1389+
jb->jitter.stats.estimate_ms = (*jb->jitter.estimate) / jb->jitter.samples_per_second * 1000;
1390+
}
1391+
1392+
if (packet_ms) {
1393+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_size_max_ms", "%u", jb->jitter.stats.size_max * packet_ms);
1394+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_size_est_ms", "%u", jb->jitter.stats.size_est * packet_ms);
1395+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_acceleration_ms", "%u", jb->jitter.stats.acceleration * packet_ms);
1396+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_expand_ms", "%u", jb->jitter.stats.expand * packet_ms);
1397+
}
1398+
1399+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_buffering_skip", "%u", jb->jitter.stats.buffering_skip);
1400+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_jitter_max_ms", "%u", jb->jitter.stats.jitter_max_ms);
1401+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_jitter_est_ms", "%u", jb->jitter.stats.estimate_ms);
1402+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_buffer_size_ms", "%u", jb->jitter.stats.buffer_size_ms);
1403+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_expand_frame_len", "%d", jb->jitter.stats.expand_frame_len);
1404+
1405+
/* Export reset statistics */
1406+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_count", "%u", jb->jitter.stats.reset);
1407+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_too_big", "%u", jb->jitter.stats.reset_too_big);
1408+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_too_expanded", "%u", jb->jitter.stats.reset_too_expanded);
1409+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_missing_frames", "%u", jb->jitter.stats.reset_missing_frames);
1410+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_ts_jump", "%u", jb->jitter.stats.reset_ts_jump);
1411+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_reset_error", "%u", jb->jitter.stats.reset_error);
1412+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_consecutive_miss", "%u", jb->jitter.stats.consecutive_miss);
1413+
1414+
/* Export video-specific statistics */
1415+
if (jb->type == SJB_VIDEO) {
1416+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_nack_saved_the_day", "%u", jb->nack_saved_the_day);
1417+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_nack_didnt_save_the_day", "%u", jb->nack_didnt_save_the_day);
1418+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_max_packet_len", "%u", jb->max_packet_len);
1419+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_packet_count", "%u", jb->packet_count);
1420+
}
1421+
1422+
/* Export elastic buffer info */
1423+
switch_channel_set_variable_printf(jb->channel, "rtp_jb_elastic", "%s", jb->elastic ? "true" : "false");
1424+
1425+
switch_mutex_unlock(jb->mutex);
1426+
}
1427+
13811428
SWITCH_DECLARE(switch_status_t) switch_jb_destroy(switch_jb_t **jbp)
13821429
{
13831430
switch_jb_t *jb = *jbp;

src/switch_rtp.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5872,6 +5872,17 @@ SWITCH_DECLARE(switch_jb_t *) switch_rtp_get_jitter_buffer(switch_rtp_t *rtp_ses
58725872
return rtp_session->jb ? rtp_session->jb : rtp_session->vb;
58735873
}
58745874

5875+
SWITCH_DECLARE(switch_jb_t *) switch_rtp_get_jitter_buffer_for_stats(switch_rtp_t *rtp_session)
5876+
{
5877+
/* Bypass ready check - used for stats export during shutdown
5878+
* when rtp_session may not be "ready" but JB still exists */
5879+
if (!rtp_session) {
5880+
return NULL;
5881+
}
5882+
5883+
return rtp_session->jb ? rtp_session->jb : rtp_session->vb;
5884+
}
5885+
58755886
SWITCH_DECLARE(switch_status_t) switch_rtp_pause_jitter_buffer(switch_rtp_t *rtp_session, switch_bool_t pause)
58765887
{
58775888
int new_val;
@@ -6399,15 +6410,19 @@ SWITCH_DECLARE(void) switch_rtp_destroy(switch_rtp_t **rtp_session)
63996410
switch_safe_free(pop);
64006411
}
64016412

6413+
/* Export jitter buffer stats before destroying them */
64026414
if ((*rtp_session)->jb) {
6415+
switch_jb_export_stats((*rtp_session)->jb);
64036416
switch_jb_destroy(&(*rtp_session)->jb);
64046417
}
64056418

64066419
if ((*rtp_session)->vb) {
6420+
switch_jb_export_stats((*rtp_session)->vb);
64076421
switch_jb_destroy(&(*rtp_session)->vb);
64086422
}
64096423

64106424
if ((*rtp_session)->vbw) {
6425+
switch_jb_export_stats((*rtp_session)->vbw);
64116426
switch_jb_destroy(&(*rtp_session)->vbw);
64126427
}
64136428

@@ -7865,15 +7880,19 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t
78657880
if (rtp_session->flags[SWITCH_RTP_FLAG_KILL_JB]) {
78667881
rtp_session->flags[SWITCH_RTP_FLAG_KILL_JB] = 0;
78677882

7883+
/* Export jitter buffer stats before destroying them */
78687884
if (rtp_session->jb) {
7885+
switch_jb_export_stats(rtp_session->jb);
78697886
switch_jb_destroy(&rtp_session->jb);
78707887
}
78717888

78727889
if (rtp_session->vb) {
7890+
switch_jb_export_stats(rtp_session->vb);
78737891
switch_jb_destroy(&rtp_session->vb);
78747892
}
78757893

78767894
if (rtp_session->vbw) {
7895+
switch_jb_export_stats(rtp_session->vbw);
78777896
switch_jb_destroy(&rtp_session->vbw);
78787897
}
78797898

0 commit comments

Comments
 (0)