Index: apps/app_confbridge.c =================================================================== --- apps/app_confbridge.c (revision 360189) +++ apps/app_confbridge.c (working copy) @@ -713,6 +713,36 @@ } /*! + * \brief Turns off MoH for a marked user when participants join + * + * \param conference_bridge Conference bridge being used + * + * \return Returns 0 on success or no-op, -1 on failure + */ +static int turn_off_marked_moh(struct conference_bridge *conference_bridge) +{ + /* Deal with the case where a marked user has hold-music while the conference is empty, + * triggered by the departure and subsequent joining of other users. */ + struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list); + if (conference_bridge->users == 2) { + /* Temporarily suspend the above participant from the bridge, if it's a marked user who can + * be on hold, so we can turn off MoH if needed. */ + if (ast_test_flag(&first_participant->u_profile, USER_OPT_MARKEDUSER) && ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD)) { + if (first_participant->playing_moh) { + if (!ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) { + first_participant->playing_moh = 0; + ast_moh_stop(first_participant->chan); + ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan); + } else { + return -1; + } + } + } + } + return 0; +} + +/*! * \brief Perform post-joining marked specific actions * * \param conference_bridge Conference bridge being joined @@ -722,6 +752,8 @@ */ static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user) { + turn_off_marked_moh(conference_bridge); + if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) { struct conference_bridge_user *other_conference_bridge_user = NULL; @@ -736,7 +768,11 @@ if (other_conference_bridge_user == conference_bridge_user) { continue; } - if (other_conference_bridge_user->playing_moh && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) { + if ( + other_conference_bridge_user->playing_moh && + ast_test_flag(&other_conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD) && + !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan) + ) { other_conference_bridge_user->playing_moh = 0; ast_moh_stop(other_conference_bridge_user->chan); ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan); @@ -779,15 +815,21 @@ return -1; } } - /* Start music on hold if needed */ - /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially - * allowing a marked user to enter while the prompt was playing - */ - if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) { + } + + /* Start music on hold if needed: MoH is enabled, the user waits for a marked user, and there are no marked users or the marked user is alone */ + /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially + * allowing a marked user to enter while the prompt was playing + */ + if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD) && + ((ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_WAITMARKED) && !conference_bridge->markedusers) || conference_bridge->users == 1) + ) { + if (!conference_bridge_user->playing_moh) { ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL); conference_bridge_user->playing_moh = 1; } } + return 0; } @@ -801,7 +843,7 @@ */ static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user) { - /* Play back audio prompt and start MOH if need be if we are the first participant */ + /* Play back audio prompt if we are the first participant */ if (conference_bridge->users == 1) { /* If audio prompts have not been quieted or this prompt quieted play it on out */ if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) { @@ -812,16 +854,38 @@ return -1; } } - /* If we need to start music on hold on the channel do so now */ - /* We need to re-check the number of users in the conference bridge here because another conference bridge - * participant could have joined while the above prompt was playing for the first user. - */ - if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) { - ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL); - conference_bridge_user->playing_moh = 1; - } - return 0; } + + /* If there are any other users without WAITMARKED set, but with MUSICONHOLD, take them off-hold + * now. */ + int nonwaiting_participants = 0; /* The number of other participants free of marked-user restrictions */ + struct conference_bridge_user *other_conference_bridge_user = NULL; + AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) { + if (other_conference_bridge_user == conference_bridge_user) { + continue; + } + if (!ast_test_flag(&other_conference_bridge_user->u_profile, USER_OPT_WAITMARKED) || + ast_test_flag(&other_conference_bridge_user->u_profile, USER_OPT_MARKEDUSER) + ) { + nonwaiting_participants += 1; + if ( + other_conference_bridge_user->playing_moh && + ast_test_flag(&other_conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD) && + !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan) + ) { + other_conference_bridge_user->playing_moh = 0; + ast_moh_stop(other_conference_bridge_user->chan); + ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan); + } + } + } + /* If there are no other unblocked users and if this user has MUSICONHOLD, turn on MoH. */ + if (!nonwaiting_participants && ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MUSICONHOLD)) { + ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL); + conference_bridge_user->playing_moh = 1; + return 0; /* No point in announcing participant-count if the user is on hold */ + } + /* Announce number of users if need be */ if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) { @@ -833,18 +897,6 @@ ao2_lock(conference_bridge); } - /* If we are the second participant we may need to stop music on hold on the first */ - if (conference_bridge->users == 2) { - struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list); - - /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */ - if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) { - first_participant->playing_moh = 0; - ast_moh_stop(first_participant->chan); - ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan); - } - } - if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) && (conference_bridge->users > conference_bridge_user->u_profile.announce_user_count_all_after)) { ao2_unlock(conference_bridge); @@ -1061,14 +1113,15 @@ /* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */ if (conference_bridge->users) { + struct conference_bridge_user *other_participant = NULL; if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER) && !conference_bridge->markedusers) { - struct conference_bridge_user *other_participant = NULL; - - /* Start out with muting everyone */ - AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) { - other_participant->features.mute = 1; - } - + /* Start out with muting everyone with WAITMARKED */ + AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) { + if (ast_test_flag(&other_participant->u_profile, USER_OPT_WAITMARKED)) { + other_participant->features.mute = 1; + } + } + /* Play back the audio prompt saying the leader has left the conference */ if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) { ao2_unlock(conference_bridge); @@ -1079,25 +1132,39 @@ ao2_lock(conference_bridge); } - /* Now on to starting MOH or kick if needed */ + /* Now on kicking participants, if needed */ AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) { if (ast_test_flag(&other_participant->u_profile, USER_OPT_ENDMARKED)) { other_participant->kicked = 1; ast_bridge_remove(conference_bridge->bridge, other_participant->chan); - } else if (ast_test_flag(&other_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) { - ast_moh_start(other_participant->chan, other_participant->u_profile.moh_class, NULL); - other_participant->playing_moh = 1; - ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan); } } - } else if (conference_bridge->users == 1) { - /* Of course if there is one other person in here we may need to start up MOH on them */ - struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list); - - if (ast_test_flag(&first_participant->u_profile, USER_OPT_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) { - ast_moh_start(first_participant->chan, first_participant->u_profile.moh_class, NULL); - first_participant->playing_moh = 1; - ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan); + } + + /* For anyone left, MoH may need to be turned on, so gather statistics */ + int nonwaiting_participants = 0; /* The number of participants not blocking on the presence of a marked user */ + if (!conference_bridge->markedusers) { /* Only count them if there are no marked users */ + AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) { + if (!other_participant->kicked && !ast_test_flag(&other_participant->u_profile, USER_OPT_WAITMARKED)) { + nonwaiting_participants += 1; + } + } + } + /* Enable MoH for anyone who should hear it */ + AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) { + if (!other_participant->kicked && ast_test_flag(&other_participant->u_profile, USER_OPT_MUSICONHOLD)) { + if (conference_bridge->users > 1) { /* If there's only one user, hold music can't be skipped */ + if (!ast_test_flag(&other_participant->u_profile, USER_OPT_WAITMARKED) && (conference_bridge->markedusers || nonwaiting_participants >= 2)) { + continue; /* All conditions needed to enable speech are satisfied */ + } else if (ast_test_flag(&other_participant->u_profile, USER_OPT_WAITMARKED) && conference_bridge->markedusers) { + continue; /* Waiting for marked user and one's already here */ + } + } + if (!ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) { + ast_moh_start(other_participant->chan, other_participant->u_profile.moh_class, NULL); + other_participant->playing_moh = 1; + ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan); + } } } } else {