Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mode-set issue when transcoding not in use #10

Open
mtryfoss opened this issue Sep 29, 2017 · 7 comments
Open

mode-set issue when transcoding not in use #10

mtryfoss opened this issue Sep 29, 2017 · 7 comments

Comments

@mtryfoss
Copy link
Contributor

Hello!

Note that this regarding how the amr format works, and not the transcoding module.

Consider this scenario:

Caller -> Asterisk -> Callee.
AMR-WB used all the way.
Caller invites with all modes available. This is also offered in Asterisk's invite to the Callee.
Callee's 183 response is with mode-set "0,1,2" only.
Asterisk will send a 183 to the Caller with all modes (which is the same as the Caller has sent in the first Invite).

The problem now is when the Caller sends a higher rate than the Callee can handle (mobile network), which for some handsets will result in distorted audio. Scenario is a VoLTE mobile calling a mobile which is on 2G or 3G.

Current workaround is changing this line:
.mode_set = 0, /* all modes /
to:
.mode_set = 7, /
mode 0,1,2 */
But that limits the possible call quality.

I assume:
The correct thing would be to pass mode "0,1,2" all the way from the Callee to the Caller? This will probably need a core change in Asterisk?

I'm not asking for a fix. Just some guidance for further debugging myself :-)

@traud
Copy link
Owner

traud commented Oct 2, 2017

Asterisk is designed not as SIP Proxy but Media Gateway. The Asterisk Team therefore claims that the SDP negotiation does not propagate between the participants but happens only with Asterisk. Therefore, it would be the job of Asterisk to transcode to lower modes. That is the theory and the official line. As you noticed, the Callee does get the mode-set of the Caller. Therefore, I am not sure what the Asterisk Team is about. Or stated differently: I am not going to hide myself behind such an explanation.

Your Caller → Callee path is one of my usual test-cases and (therefore) works. If I am not totally mistaken, I tested the reverse – your mentioned scenario – at least once as well. I thought it works. To debug this yourself, I recommend to print/log the pointer address of all ast_format via ast_log(LOG_WARNING, "%p\n", …. Start in res_format_attr_amr.c:amr_getjoint and double-check with res_format_attr_amr.c:amr_generate_sdp_fmtp. If the address changed, something went wrong between these two calls. Perhaps my joiner does not work correctly or somewhere in Asterisk the ast_format is copied (without its attribute). Could even be your dialplan extensions.conf. To rule that out, reduce it to an absolute minimum.

I had to patch/fix a lot code parts which killed the format attributes unintentionally. Just recently, I found another one. All known issues are fixed in the current Asterisk release. Nevertheless:

  1. Which version/variant of Asterisk do you use – 13.17.x?
  2. Which channel driver do you use – chan_sip?

@mtryfoss
Copy link
Contributor Author

mtryfoss commented Oct 2, 2017

Thanks for the feedback! I'm not at work this week, but I'll get back to you after I've had time to debug more.

We use a custom version (to fit in our IMS network) based on 13.8, with a lot of newer relevant commits backported. I will check if your patches is in or not.
chan_sip is used.

Another question. It seems like you've good knowledge for Asterisk and RTP stuff. Would you be interested in doing some paid work for our company? We currently do not have any specific cases, but we're interested getting in contact with experts in different fields. I work for a company owned by a large mobile network operator.

@mtryfoss
Copy link
Contributor Author

mtryfoss commented Oct 3, 2017

Investigated a little bit further.

What I see:

  • A format is cloned in amr_parse_sdp_fmtp for the incoming INVITE.
  • The same format is used in amr_generate_sdp_fmtp for the outgoing INVITE.
  • A cloned format is used in amr_parse_sdp_fmtp (triggered by the 183 response).
  • The two formats are being compared in the subsequent getjoint (it knows that one of the formats do have mode-set limitations).

However, in the amr_generate_sdp_fmtp for forwarding the 183 the first format is used, and here there is no limitation of mode-set again.

This hack in the getjoint function gives the correct reply to the caller:
} else if (0 == attr1->mode_set && 0 != attr2->mode_set) {
attr_res->mode_set = attr2->mode_set; /* attr1 allowed all */
+ attr1->mode_set = attr2->mode_set;
}

Questions/Thoughts:

  • Are the parsed fmtp lines of the 183 reply supposed to change the mode_set of the first format, or is the behaviour above correct? If so, should there be a compare of mode-set in the fmtp generate function?
  • I guess this is only an issue when not transcoding, since when transcoding you'll only have this amr format on one of the call legs. Aren't all changes from one side supposed to be reflected to the other when there's no transcoding in use?
  • Maybe the second cloned format should be used for generation of the last reply when there's no transcoding? So each format is used all places in the same "direction" for both call legs.

@traud
Copy link
Owner

traud commented Oct 4, 2017

Are the parsed fmtp lines of the 183 reply supposed to …

With SIP status 183, you are about the answer (SDP response) of the Callee?

[Callee’s SDP response] change the mode_set of the first format

When it comes to the implementation in Asterisk, an existing ast_format (or its attribute) may not be changed but a new ast_format (with new attribute) must be created/returned instead. That is an architectural thing. From my experience, if you do not change a ‘cached’ ast_format, your are fine. However, you won’t be able to submit such a change into Asterisk because the Asterisk Team sees a ast_format (and all structures included) as immutable ‘objects’. Those may only be copied/replaced at runtime, their value/content must not be edited.

Should there be a compare of mode-set in the fmtp generate function?

Not sure, I got that question.
A try: The generate function cannot compare because there is just one format as input. That function simply generates the SDP lines from the inputted format.
However, all answers/output from Asterisk should be a joined format by now. Currently why ever, Asterisk sends the joined format between the Caller/Asterisk always – and not the joined format between Caller/Callee.

Aren't all changes from one side supposed to be reflected to the other when there's no transcoding in use?

FreeSWITCH, yes. Asterisk, no.
Asterisk is a media gateway without transparent SDP negotiation. Asterisk negotiates with each party (call leg) separately. Why ever. That was the design of Asterisk. However since version 13, Asterisk is able to propagate/forward the fmtp to the callee. I re-tested your scenario today, and (why ever) Asterisk does not send that back to the originating caller. As you stated. So, I am able to confirm your issue even with Asterisk 13.17. Furthermore in my test, this fmtp propagation to the callee worked only for the very first format of the caller. Consequently, there is an additional issue.

Or stated differently: From the point of Asterisk, you have a transcoding situation actually. Asterisk should transcode but my AMR module is not designed to do so in a scenario like yours. Consequently, one approach is to get that SDP negotiation going. Another one would be to transcode even in such a case.

Aren't all changes from one side supposed to be reflected to the other when there's no transcoding in use?

There is a hot debate in the SIP community, whether SDP negotiation (of AMR) works that way at all, whether the Callee is able to change the incoming request in his answer/response at all. Some claim that there is only accept/reject and changes were not allowed. Therefore, your call would not be created at all, because the Caller did not offer a mode-set compatible with your Callee. When you look at newer RFCs and 3GPP EVS, you get a picture of that mess. For example, according to those, you would have to create at least two rtpmaps, one fmtp with octet-align=0 and another with octet-align=1. I am not aware of any reason for that. SDP is able to negotiate that. If the caller is specifying something, he allows all. If the caller allows a set, the callee is able to go for a subset. So yes, I am on your side. 3GPP, ETSI, and IETF are not.

Maybe the second cloned format should be used for generation of the last reply when there's no transcoding? So each format is used all places in the same "direction" for both call legs.

I have to double-check what is wrong exactly or whether this is possible with Asterisk at all. Currently, Asterisk uses the joined format between Caller/Asterisk in both generate_sdp_fmtp.

Would you be interested in doing some paid work for our company?

I sent you an E-mail to discuss this off-GitHub.

@mtryfoss
Copy link
Contributor Author

mtryfoss commented Oct 5, 2017

With SIP status 183, you are about the answer (SDP response) of the Callee?

Yes.

Or stated differently: From the point of Asterisk, you have a transcoding situation actually. Asterisk should transcode but my AMR module is not designed to do so in a scenario like yours. Consequently, one approach is to get that SDP negotiation going. Another one would be to transcode even in such a case.

Could we implement a compare function for the AMR format as well, like for example in the SILK format? SILK also compares sample rate to report them as equal. We could do this for (at least) mode-set. Of course, this will burn extra cpu and potentially add delay, distortion etc. Most of my calls do not involve transcoding today.

one fmtp with octet-align=0 and another with octet-align=1

I already have this in the IMS network. Had to make a workaround ignoring octet-aligned (bw efficient is supported by all devices anyway), or else I had a situation where Asterisk choose octet-align on one leg and bw efficient on the other - without transcoding.

I will dig more into this when I'm back at work next week.

@mtryfoss
Copy link
Contributor Author

I've look a bit more into this. However, I'm not quite sure how it's intended to work.

What seems reasonable to me for a simple call through asterisk with AMR-WB all the way:

  • Asterisk receives an INVITE from the caller. The SDP is parsed and an AMR-WB format with attributes is created/cloned.
  • Asterisk clones this format and uses the newly cloned format for the subsequent INVITE to the callee.
  • Asterisk receives 183 and/or 200 with SDP from callee, parses this and clones a new format with the attributes the callee wants to use.
  • Asterisk then will use the attributes received from the callee to clone a fourth format with attributes. Then it should get a joint format between this and the first created to send 183 and/or 200 with SDP with the combined attributes to the caller.

There will in the end (after call has early media or is answered) be two formats containing attributes for each call leg (4 in total).

However, this is what it seems to do now:

  • Asterisk receives an INVITE from the caller. The SDP is parsed and an AMR-WB format with attributes is created/cloned.
  • Asterisk uses the same format with attributes from the INVITE from the caller to generate a new INVITE to the callee.
  • Asterisk receives a 183 and/or 200 from the callee with SDP. It clones the format, parses and stores attributes.
  • Asterisk then will send a 183 and/or 200 to the caller with SDP. Here it will only reply with the same attribute as is cloned in the first INVITE from the caller. So, Asterisk will only respond with whatever the caller wants - except if that differs from the default AMR/AMR-WB values defined in 'default_amr_attr'.

So, for the first call leg there's only one format. For the second call leg there two. The one from the first leg and the one created by 'amr_parse_sdp_fmtp'.

Possible solutions?

  • Some way of signaling attributes from callee to caller using Asterisk control frames? This could be done by implementing 'format_attribute_set' for the interface and calling this from chan_sip. I notice this seems to be defined for Opus, but I can't find it used anywhere.
    For this approach we would probably need to replace the original format with the one returned from this function, or else 'amr_generate_sdp_fmtp' will just use the original.
  • Using the returned cloned format from 'amr_parse_sdp_fmtp' to replace the original format? This could only be done at the second (outgoing) call leg side of Asterisk. Will this be reflected to the first (incoming) side of Asterisk? I think not?

@mtryfoss
Copy link
Contributor Author

Hello!

I made a proof of concept of something like approach 1 above. This is dirty work, but it seems to do the trick.
https://gist.github.com/mtryfoss/3f84daa607993367c029ce3976527303

In short:
-After parsing fmtp for AMR-WB, I'm queueing a new frame type (AST_FRAME_FORMAT) with f.subclass.format set to the new format returned by ast_format_parse_sdp_fmtp().
-This traverse through the Asterisk core and is handled by chan_sip on the other side.

  • I do a simple ast_format_cap_replace_from_cap(), replacing p->jointcaps and p->caps with the format in the frame.

I only replace format in the backwards direction (after parsing fmtp for outgoing_calls only).

I would really like your opinion :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants