@ -1064,6 +1064,25 @@ struct ast_channel *__ast_dummy_channel_alloc(const char *file, int line, const
return tmp ;
}
void ast_channel_start_defer_frames ( struct ast_channel * chan )
{
ast_set_flag ( ast_channel_flags ( chan ) , AST_FLAG_DEFER_FRAMES ) ;
}
void ast_channel_stop_defer_frames ( struct ast_channel * chan )
{
ast_clear_flag ( ast_channel_flags ( chan ) , AST_FLAG_DEFER_FRAMES ) ;
/* Move the deferred frames onto the channel read queue, ahead of other queued frames */
ast_queue_frame_head ( chan , AST_LIST_FIRST ( ast_channel_deferred_readq ( chan ) ) ) ;
/* ast_frfree will mosey down the list and free them all */
if ( ! AST_LIST_EMPTY ( ast_channel_deferred_readq ( chan ) ) ) {
ast_frfree ( AST_LIST_FIRST ( ast_channel_deferred_readq ( chan ) ) ) ;
}
/* Reset the list to be empty */
AST_LIST_HEAD_INIT_NOLOCK ( ast_channel_deferred_readq ( chan ) ) ;
}
static int __ast_queue_frame ( struct ast_channel * chan , struct ast_frame * fin , int head , struct ast_frame * after )
{
struct ast_frame * f ;
@ -1527,19 +1546,18 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c
int res = 0 ;
struct timeval start ;
int ms ;
AST_LIST_HEAD_NOLOCK ( , ast_frame ) deferred_frames ;
AST_LIST_HEAD_INIT_NOLOCK ( & deferred_frames ) ;
/* If no other generator is present, start silencegen while waiting */
if ( ast_opt_transmit_silence & & ! ast_channel_generatordata ( chan ) ) {
silgen = ast_channel_start_silence_generator ( chan ) ;
}
ast_channel_lock ( chan ) ;
ast_channel_start_defer_frames ( chan ) ;
ast_channel_unlock ( chan ) ;
start = ast_tvnow ( ) ;
while ( ( ms = ast_remaining_ms ( start , timeout_ms ) ) ) {
struct ast_frame * dup_f = NULL ;
if ( cond & & ( ( * cond ) ( data ) = = 0 ) ) {
break ;
}
@ -1554,18 +1572,7 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c
res = - 1 ;
break ;
}
if ( ! ast_is_deferrable_frame ( f ) ) {
ast_frfree ( f ) ;
continue ;
}
if ( ( dup_f = ast_frisolate ( f ) ) ) {
if ( dup_f ! = f ) {
ast_frfree ( f ) ;
}
AST_LIST_INSERT_HEAD ( & deferred_frames , dup_f , frame_list ) ;
}
ast_frfree ( f ) ;
}
}
@ -1574,17 +1581,8 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int timeout_ms, int (*c
ast_channel_stop_silence_generator ( chan , silgen ) ;
}
/* We need to free all the deferred frames, but we only need to
* queue the deferred frames if there was no error and no
* hangup was received
*/
ast_channel_lock ( chan ) ;
while ( ( f = AST_LIST_REMOVE_HEAD ( & deferred_frames , frame_list ) ) ) {
if ( ! res ) {
ast_queue_frame_head ( chan , f ) ;
}
ast_frfree ( f ) ;
}
ast_channel_stop_defer_frames ( chan ) ;
ast_channel_unlock ( chan ) ;
return res ;
@ -3885,6 +3883,32 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
if ( ! AST_LIST_EMPTY ( ast_channel_readq ( chan ) ) ) {
int skip_dtmf = should_skip_dtmf ( chan ) ;
if ( ast_test_flag ( ast_channel_flags ( chan ) , AST_FLAG_DEFER_FRAMES ) ) {
AST_LIST_TRAVERSE_SAFE_BEGIN ( ast_channel_readq ( chan ) , f , frame_list ) {
if ( ast_is_deferrable_frame ( f ) ) {
if ( f - > frametype = = AST_FRAME_CONTROL & & f - > subclass . integer = = AST_CONTROL_HANGUP ) {
struct ast_frame * dup ;
/* Hangup is a special case. We want to defer the frame, but we also do not
* want to remove it from the frame queue . So rather than just moving the frame
* over , we duplicate it and move the copy to the deferred readq .
*
* The reason for this ? This way , whoever calls ast_read ( ) will get a NULL return
* immediately and can tell the channel has hung up and do what it needs to . Also ,
* when frame deferral finishes , then whoever calls ast_read ( ) next will also get
* the hangup .
*/
dup = ast_frdup ( f ) ;
AST_LIST_INSERT_TAIL ( ast_channel_deferred_readq ( chan ) , dup , frame_list ) ;
} else {
AST_LIST_INSERT_TAIL ( ast_channel_deferred_readq ( chan ) , f , frame_list ) ;
AST_LIST_REMOVE_CURRENT ( frame_list ) ;
}
}
}
AST_LIST_TRAVERSE_SAFE_END ;
}
AST_LIST_TRAVERSE_SAFE_BEGIN ( ast_channel_readq ( chan ) , f , frame_list ) {
/* We have to be picky about which frame we pull off of the readq because
* there are cases where we want to leave DTMF frames on the queue until
@ -10216,9 +10240,15 @@ int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struc
ast_party_connected_line_copy ( ast_channel_connected ( macro_chan ) , connected ) ;
}
ast_channel_start_defer_frames ( macro_chan ) ;
ast_channel_unlock ( macro_chan ) ;
retval = ast_app_run_macro ( autoservice_chan , macro_chan , macro , macro_args ) ;
ast_channel_lock ( macro_chan ) ;
ast_channel_stop_defer_frames ( macro_chan ) ;
ast_channel_unlock ( macro_chan ) ;
if ( ! retval ) {
struct ast_party_connected_line saved_connected ;
@ -10266,9 +10296,15 @@ int ast_channel_redirecting_macro(struct ast_channel *autoservice_chan, struct a
ast_party_redirecting_copy ( ast_channel_redirecting ( macro_chan ) , redirecting ) ;
}
ast_channel_start_defer_frames ( macro_chan ) ;
ast_channel_unlock ( macro_chan ) ;
retval = ast_app_run_macro ( autoservice_chan , macro_chan , macro , macro_args ) ;
ast_channel_lock ( macro_chan ) ;
ast_channel_stop_defer_frames ( macro_chan ) ;
ast_channel_unlock ( macro_chan ) ;
if ( ! retval ) {
struct ast_party_redirecting saved_redirecting ;
@ -10309,9 +10345,15 @@ int ast_channel_connected_line_sub(struct ast_channel *autoservice_chan, struct
ast_party_connected_line_copy ( ast_channel_connected ( sub_chan ) , connected ) ;
}
ast_channel_start_defer_frames ( sub_chan ) ;
ast_channel_unlock ( sub_chan ) ;
retval = ast_app_run_sub ( autoservice_chan , sub_chan , sub , sub_args , 0 ) ;
ast_channel_lock ( sub_chan ) ;
ast_channel_stop_defer_frames ( sub_chan ) ;
ast_channel_unlock ( sub_chan ) ;
if ( ! retval ) {
struct ast_party_connected_line saved_connected ;
@ -10352,9 +10394,15 @@ int ast_channel_redirecting_sub(struct ast_channel *autoservice_chan, struct ast
ast_party_redirecting_copy ( ast_channel_redirecting ( sub_chan ) , redirecting ) ;
}
ast_channel_start_defer_frames ( sub_chan ) ;
ast_channel_unlock ( sub_chan ) ;
retval = ast_app_run_sub ( autoservice_chan , sub_chan , sub , sub_args , 0 ) ;
ast_channel_lock ( sub_chan ) ;
ast_channel_stop_defer_frames ( sub_chan ) ;
ast_channel_unlock ( sub_chan ) ;
if ( ! retval ) {
struct ast_party_redirecting saved_redirecting ;