flutter-webrtc / dart-sip-ua

A dart-lang version of the SIP UA stack.
MIT License
333 stars 257 forks source link

Question: Is it possible to do an attended transfer? #198

Closed medexdev closed 3 years ago

medexdev commented 3 years ago

Instead of a blind transfer, is it possible to do an attended transfer? I think jsSIP supports it.

cloudwebrtc commented 3 years ago

https://github.com/flutter-webrtc/dart-sip-ua/blob/master/lib/src/sip_ua_helper.dart#L395 It should be supported, but these APIs still need to be exported and tested.

medexdev commented 3 years ago

Please forgive me if I'm mistaken. sip_ua_helper calls rtc_session.refer() which calls ReferSubscriber.sendRefer(). But ReferSubscriber only takes URI target instead of RTCSession target. According to this documentation on sipjs, do we need to pass a RTCSession target if we want to do an attender transer? https://sipjs.com/guides/transfer/

medexdev commented 3 years ago

Looking at the code again, I see that the replace is in the option. So it looks like it's supported. Let me give it a try.

medexdev commented 3 years ago

After exposing a few private variables through getters, I was able to get attended transfer working.

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

eScoops commented 1 year ago

After exposing a few private variables through getters, I was able to get attended transfer working.

What exactly did you need to do to get this to work?

medexdev commented 1 year ago

After exposing a few private variables through getters, I was able to get attended transfer working.

What exactly did you need to do to get this to work?

I did not end up using this option. But it was adding this in sip_ua_helper.dart.refer() method.

  void refer(dynamic target, [Map<String, dynamic> options]) {
    assert(_session != null, 'ERROR(refer): rtc session is invalid!');
    ReferSubscriber refer = _session.refer(target, options);

Then passing the options to rtc_session (which already takes it as an optional arg). Then you call refer with the 'replaces' option. If you look the source code for refer_subscriber.dart, you can see that it checks for options['replaces'].

    if (options['replaces'] != null) {
      replaces = options['replaces'].call_id;
      replaces += ';to-tag=${options['replaces'].to_tag}';
      replaces += ';from-tag=${options['replaces'].from_tag}';
      replaces = Utils.encodeURIComponent(replaces);
    }
melio-matt commented 1 year ago

Hello, I've followed the changes mentioned on altering sip_ua_helper.dart which is straight forward. Where do you get the to_tag and from_tag from, I've got access to both sessions of the two calls and am trying to refer one to the other?

I can see how I can implement this by create another function in my code that replicates the sip_ua_helper.dart's function so I can call the session's refer method directly and not have to alter the code. It would be good if the code base contained a method to do this though.

DeviArunaMurugan commented 1 year ago

If you share the code for attended call transfer would be appreciated.

Thanks in advance.

DeviArunaMurugan commented 1 year ago

If you share the code for attended call transfer would be appreciated.

melio-matt commented 1 year ago

What I did was to add a getFromTag and getToTag that return _from_tag and _to_tag in the RTCSession object (rtc_session.dart).

Then from one of the calls (Call class) call the refer function as below:

// the call id has the from tag appended to it so we need to remove 
String callId = transferingToCall.session.id!;
callId = callId.substring(0, callId.length - fromTag.length);

transferingCall.refer(transferingToCall.remote_identity!, {
                            "replaces": AttendedTransferData(callId, fromTag, toTag),
                          });

I previously had one of the calls on hold and was speaking to the other call at the time.

Vinayak006 commented 8 months ago

Does anyone implement call transfer? If so, could you please explain how to implement it? I am using the Call.refer(target) for call transfer, as there are no options I have changed the refer function in sip_ua_helper.dart and given options like call_id, to_tag, and from_tag. Even did so, I wouldn't receive CallStateEnum.REFER in onCallStateChanged or onNotify. Help me out on it.

String fromtag = currentCall!.session.from_tag!;
String totag = currentCall!.session.to_tag!;
String callId = currentCall!.session.id!;
callId = callId.substring(0, callId.length - fromtag.length);
currentCall?.refer(target, {
      "replaces": CallTransfer(
        call_id: callId,
        to_tag: totag,
        from_tag: fromtag,
      )
    });
melio-matt commented 8 months ago

@Vinayak006 I presume you are trying to do an attended transfer, however from your code you only appear to have one call involved (currentCall). The fromTag and the toTag should be from the call that want to the currentCall to be transferred to, not the currentCall. I've never actually look to see if CallStateEnum.REFER is emitted, but I can perform attended transfers.

Vinayak006 commented 8 months ago

@melio-matt Then, for Blind Transfer?

melio-matt commented 8 months ago

@Vinayak006 With a blind transfer you don't have another active call at this point, so transferingCall.refer(phoneNumberToTransferTo); Just pass the phone number you want to transfer to.

In an attended transfer I'd already placed the first caller on hold and then started a new call to the second caller. The difference between attended and blind transfers from the code perspective, not the user's perspective, is if you have 2 calls going and want to transfer one to the other. Where as blind is that you only have one call active when you make the decision to transfer.

Vinayak006 commented 8 months ago

@melio-matt Sorry melio, But when I do currentCall.refer(target), what can I expect I don't see any CallStateEnum.CALL_INITIATION or any CallState changed in the target. what am I doing wrong? Do you have any code snippet?

melio-matt commented 8 months ago

@Vinayak006 You shouldn't see call initiation as you are transferring the other caller away from yourself to someone else. Essentially your call will end when the refer has been called as you are no longer going to be part of the conversation. You should see a CallStateEnum.ENDED, REFER might be called first, though I've never checked to see if it is.

Vinayak006 commented 8 months ago

@melio-matt Yeah the call is ending as expected but the Referring is failing with the below logs

flutter: [2023-12-27 13:31:43.205] Level.debug refer_subscriber.dart:120 ::: REFER failed
flutter: [2023-12-27 13:31:43.206] Level.debug refer_subscriber.dart:122 ::: emit "requestFailed"
flutter: [2023-12-27 13:31:43.206] Level.error event_manager.dart:111 ::: type 'String' is not a subtype of type 'ErrorCause?'
flutter: #0      ReferSubscriber._requestFailed (package:sip_ua/src/rtc_session/refer_subscriber.dart:124:61)
#1      ReferSubscriber.sendRefer.<anonymous closure> (package:sip_ua/src/rtc_session/refer_subscriber.dart:64:7)
#2      EventManager.emit (package:sip_ua/src/event_manager/event_manager.dart:109:17)
#3      DialogRequestSender.send.<anonymous closure> (package:sip_ua/src/dialog/request_sender.dart:38:22)
#4      EventManager.emit (package:sip_ua/src/event_manager/event_manager.dart:109:17)
#5      RequestSender.send.<anonymous closure> (package:sip_ua/src/request_sender.dart:48:22)
#6      EventManager.emit (package:sip_ua/src/event_manager/event_manager.dart:109:17)
#7      NonInviteClientTransaction.timer_F (package:sip_ua/src/transactions/non_invite_client.dart:65:20)
#8      NonInviteClientTransaction.send.<anonymous closure> (package:sip_ua/src/transactions/non_invite_client.dart:43:7)
#9      Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
#10     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19)
#11     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5)
#12     _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
melio-matt commented 8 months ago

@Vinayak006 The simple passing of an extension number to the refer method is all I'm doing in my Flutter code. You may have to look at the SIP output between your client and server. sip_ua outputs to standard out all the sip messages back a forth so you might be able to see what is going on. I don't know if you have access to your sip server or not, often comparing what a working phone does to what you are doing in terms of message passing can be useful.

Vinayak006 commented 8 months ago

I am using asterisk test server provided in the documentation. Anyway thanks @melio-matt.