@@ -400,6 +400,15 @@ static switch_bool_t is_silence_frame(switch_frame_t *frame, int silence_thresho
400400 return is_silence ;
401401}
402402
403+ static switch_status_t write_hold_generated_silence (switch_core_session_t * session_b , switch_frame_t * silence_frame ,
404+ switch_codec_implementation_t * read_impl , int silence_val , int stream_id )
405+ {
406+ switch_generate_sln_silence ((int16_t * ) silence_frame -> data , silence_frame -> samples ,
407+ read_impl -> number_of_channels , silence_val );
408+
409+ return switch_core_session_write_frame (session_b , silence_frame , SWITCH_IO_FLAG_FORCE , stream_id );
410+ }
411+
403412
404413SWITCH_DECLARE (void ) switch_ivr_bridge_display (switch_core_session_t * session , switch_core_session_t * peer_session )
405414{
@@ -440,6 +449,7 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
440449 const char * silence_var ;
441450 const char * continuous_silence_var ;
442451 int silence_val = 0 , bypass_media_after_bridge = 0 , max_continuous_silence_ms = 0 , silence_threshold = 0 ;
452+ int silence_codec_initialized = 0 , generate_silence_on_hold = 0 ;
443453 const char * bridge_answer_timeout = NULL ;
444454 int bridge_filter_dtmf , answer_timeout , sent_update = 0 ;
445455 time_t answer_limit = 0 ;
@@ -625,6 +635,7 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
625635
626636 silence_val = 0 ;
627637 } else {
638+ silence_codec_initialized = 1 ;
628639 switch_log_printf (SWITCH_CHANNEL_SESSION_LOG (session_a ), SWITCH_LOG_DEBUG , "Setup generated silence from %s to %s at %d\n" , switch_channel_get_name (chan_a ),
629640 switch_channel_get_name (chan_b ), silence_val );
630641 silence_frame .codec = & silence_codec ;
@@ -640,11 +651,40 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
640651#endif
641652 }
642653
654+ if (switch_channel_var_true (chan_b , "bridge_generate_silence_on_hold" )) {
655+ generate_silence_on_hold = 1 ;
656+
657+ if (!silence_val ) {
658+ silence_val = 1400 ;
659+
660+ if (switch_core_codec_init (& silence_codec ,
661+ "L16" ,
662+ NULL ,
663+ NULL ,
664+ read_impl .actual_samples_per_second ,
665+ read_impl .microseconds_per_packet / 1000 ,
666+ 1 ,
667+ SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE ,
668+ NULL , switch_core_session_get_pool (session_a )) != SWITCH_STATUS_SUCCESS ) {
669+ silence_val = 0 ;
670+ generate_silence_on_hold = 0 ;
671+ } else {
672+ silence_codec_initialized = 1 ;
673+ silence_frame .codec = & silence_codec ;
674+ silence_frame .data = silence_data ;
675+ silence_frame .buflen = sizeof (silence_data );
676+ silence_frame .datalen = read_impl .decoded_bytes_per_packet ;
677+ silence_frame .samples = silence_frame .datalen / sizeof (int16_t );
678+ }
679+ }
680+ }
681+
643682 bridge_filter_dtmf = switch_true (switch_channel_get_variable (chan_a , "bridge_filter_dtmf" ));
644683
645684
646685 for (;;) {
647686 int sanity = 1000 ;
687+ int source_held = 0 , target_held = 0 ;
648688 switch_channel_state_t b_state ;
649689 switch_status_t status ;
650690 switch_event_t * event ;
@@ -792,6 +832,9 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
792832
793833 switch_ivr_parse_all_messages (session_a );
794834
835+ source_held = switch_channel_test_flag (chan_a , CF_HOLD ) || switch_channel_test_flag (chan_a , CF_LEG_HOLDING );
836+ target_held = switch_channel_test_flag (chan_b , CF_HOLD ) || switch_channel_test_flag (chan_b , CF_LEG_HOLDING );
837+
795838 if (!inner_bridge && (switch_channel_test_flag (chan_a , CF_SUSPEND ) || switch_channel_test_flag (chan_b , CF_SUSPEND ))) {
796839#if DEBUG_RTP
797840 switch_log_printf (SWITCH_CHANNEL_SESSION_LOG (session_a ), SWITCH_LOG_NOTICE , "Audio bridge thread: #21 %p -> %p\n" , (void * )session_a , (void * )session_b );
@@ -807,6 +850,15 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
807850#endif
808851 goto end_of_bridge_loop ;
809852 }
853+
854+ /* Hold may set CF_SUSPEND; protocol hold may only set CF_LEG_HOLDING.
855+ Use the held read (often SWITCH_STATUS_BREAK) only as the timer tick
856+ for generated silence, and never write toward a held peer. */
857+ if (generate_silence_on_hold && source_held && !target_held ) {
858+ if (write_hold_generated_silence (session_b , & silence_frame , & read_impl , silence_val , stream_id ) != SWITCH_STATUS_SUCCESS ) {
859+ goto end_of_bridge_loop ;
860+ }
861+ }
810862 continue ;
811863 }
812864#if DEBUG_RTP
@@ -1136,7 +1188,7 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
11361188 continue ;
11371189 }
11381190
1139- if (status != SWITCH_STATUS_BREAK && !switch_channel_test_flag ( chan_a , CF_HOLD ) && !switch_channel_test_flag ( chan_b , CF_LEG_HOLDING ) ) {
1191+ if (status != SWITCH_STATUS_BREAK && !source_held && !target_held ) {
11401192#if DEBUG_RTP
11411193 switch_log_printf (SWITCH_CHANNEL_SESSION_LOG (session_a ), SWITCH_LOG_NOTICE , "Audio bridge thread: write frame %p -> %p\n" , (void * )session_a , (void * )session_b );
11421194#endif
@@ -1145,6 +1197,11 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
11451197 "%s ending bridge by request from write function\n" , switch_channel_get_name (chan_b ));
11461198 goto end_of_bridge_loop ;
11471199 }
1200+ } else if (generate_silence_on_hold && source_held && !target_held ) {
1201+ /* Held reads can return SWITCH_STATUS_BREAK; treat BREAK as pacing for generated silence only. */
1202+ if (write_hold_generated_silence (session_b , & silence_frame , & read_impl , silence_val , stream_id ) != SWITCH_STATUS_SUCCESS ) {
1203+ goto end_of_bridge_loop ;
1204+ }
11481205 }
11491206 } else {
11501207 switch_log_printf (SWITCH_CHANNEL_SESSION_LOG (session_a ), SWITCH_LOG_DEBUG , "%s ending bridge by request from read function\n" , switch_channel_get_name (chan_a ));
@@ -1175,7 +1232,7 @@ static void *audio_bridge_thread(switch_thread_t *thread, void *obj)
11751232#endif
11761233
11771234
1178- if (silence_val ) {
1235+ if (silence_codec_initialized ) {
11791236 switch_core_codec_destroy (& silence_codec );
11801237 }
11811238
0 commit comments