signalwire / freeswitch

FreeSWITCH is a Software Defined Telecom Stack enabling the digital transformation from proprietary telecom switches to a versatile software implementation that runs on any commodity hardware. From a Raspberry PI to a multi-core server, FreeSWITCH can unlock the telecommunications potential of any device.
https://freeswitch.com/#getting-started
Other
3.56k stars 1.41k forks source link

Music on hold not played after transferring a remotely held call #2230

Open fmfig opened 1 year ago

fmfig commented 1 year ago

Describe the bug When a remotely held call is transferred to another user, the music on hold is not played.

To Reproduce Steps to reproduce the behavior:

  1. User A calls User B
  2. User B answers the call
  3. User A puts the call on hold, User B hears music on hold
  4. User B transfers the call to User C

Expected behavior User C should hear the music on hold.

Package version or git hash

Trace logs Not available. However, I did some preliminary analysis of the code, which hopefully will help (please see Analysis sections below).

Analysis (part 1) When the INVITE is sent towards User C, the INVITE is sent with a=sendonly. I traced this back to switch_core_media.c:10374:

switch_core_session_t *orig_session = NULL;

switch_core_session_get_partner(session, &orig_session);

if (orig_session && !switch_channel_test_flag(session->channel, CF_ANSWERED)) {
    switch_core_media_set_smode(smh->session, SWITCH_MEDIA_TYPE_AUDIO,
                                switch_core_session_remote_media_flow(orig_session, SWITCH_MEDIA_TYPE_AUDIO), sdp_type);
    switch_core_media_set_smode(smh->session, SWITCH_MEDIA_TYPE_VIDEO,
                                switch_core_session_remote_media_flow(orig_session, SWITCH_MEDIA_TYPE_VIDEO), sdp_type);
}

This code seems to take the media flow (SWITCH_MEDIA_FLOW_SENDONY) from the orig_session.

☁️ Since “music on hold” is configured, I think that the INVITE shouldn’t rely on the orig_session, but instead send always sendrecv. This is at least what happens when a regular call is placed on hold: the callee does not receive a re-INVITE with the a=sendonly (he is not informed that the caller held the call).

Analysis (part 2) When the call is transferred, User A is receiving a re-INVITE request, which is replied with a a=sendonly:

o=- 3901021402 3901021404 IN IP4 172.16.0.1
s=pjmedia
b=AS:84
t=0 0
a=X-nat:0
m=audio 4000 RTP/AVP 9 121
c=IN IP4 172.16.0.1
b=TIAS:64000
a=rtcp:4001 IN IP4 172.16.0.1
a=rtpmap:9 G722/8000
a=rtpmap:121 telephone-event/8000
a=fmtp:121 0-16
a=ssrc:2019046580 cname:63ac66bf4b8db8b8
a=sendonly
a=oldmediaip:192.168.1.67
a=oldmediaip:192.168.1.67
a=oldmediaip:192.168.1.67
2023-08-14 17:03:33.339 [DEBUG] switch_ivr_bridge.c:915 BRIDGE THREAD DONE [sofia/sipinterface_1/alice-dev-0@local.app.net]
2023-08-14 17:03:33.339 [NOTICE] switch_ivr_bridge.c:930 Hangup sofia/sipinterface_1/bob-dev-0@local.app.net [CS_EXCHANGE_MEDIA] [NORMAL_CLEARING]
2023-08-14 17:03:33.339 [DEBUG] switch_channel.c:2161 (sofia/sipinterface_1/alice-dev-0@local.app.net) Callstate Change HELD -> UNHELD

I traced back the function call that makes the callstate changing from HELD → UNHELD to the instruction switch_channel_mark_hold(chan_a, SWITCH_FALSE); in switch_ivr_bridge.c:

    if (switch_channel_test_flag(chan_a, CF_LEG_HOLDING) || switch_channel_test_flag(chan_a, CF_HANGUP_HELD)) {
        if (switch_channel_ready(chan_b) &&
            switch_channel_get_state(chan_b) != CS_PARK && !data->other_leg_data->clean_exit && !switch_channel_test_flag(chan_b, CF_3P_NOMEDIA_REQUESTED)) {
            const char *ext = switch_channel_get_variable(chan_a, "hold_hangup_xfer_exten");

            switch_channel_stop_broadcast(chan_b);

            if (zstr(ext)) {
                switch_call_cause_t cause = switch_channel_get_cause(chan_b);
                if (cause == SWITCH_CAUSE_NONE) {
                    cause = SWITCH_CAUSE_NORMAL_CLEARING;
                }
                switch_channel_hangup(chan_b, cause);
            } else {
                switch_channel_set_variable(chan_b, SWITCH_TRANSFER_AFTER_BRIDGE_VARIABLE, ext);
            }
        }

        if (switch_channel_test_flag(chan_a, CF_LEG_HOLDING)) {
            switch_channel_mark_hold(chan_a, SWITCH_FALSE);
        }
    }

This code was added in this commit, to solve FS-6272. I couldn’t find information regarding this issue, except for this:

f862c34 fixed bug in Freeswitch core dealing with calls not hanging up 
after hold
Jira: http://jira.freeswitch.org/browse/FS-6272

☁️ The strange thing here is that the channel being hanged up is the channel for User B, not User A, as shown in the log above. I also cannot understand why the channel needs to be unheld, as User A kept the call on hold.

silviucpp commented 10 months ago

I'm facing same issue , any news on this ?