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

core,app_dial,pjsip: Implement Advanced Codec Negotiation (ACN) #285

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

maximilianfridrich
Copy link
Contributor

Advanced Codec Negotiation is now implemented for the dial application, pjsip channels and local channels.

Resolves: #223

UpgradeNote: The two pjsip.conf options "incoming_call_offer_pref" and "outgoing_call_offer_pref" have been removed. Instead, the new options "codec_prefs_incoming_answer", "codec_prefs_incoming_offer", "codec_prefs_outgoing_answer", "codec_prefs_outgoing_offer" must be used. Even if the old settings were not used, this could break existing deployments as the default ACN options are now in use and they might behave differently in some call flows (e.g. by default, pending codecs are always preferred, not the endpoint configuration).

UserNote: The Advanced Codec Negotiation feature is now implemented.

@maximilianfridrich
Copy link
Contributor Author

cherry-pick-to: 21
cherry-pick-to: 20
cherry-pick-to: 18

@jcolp
Copy link
Member

jcolp commented Aug 30, 2023

I haven't looked at the code, just the upgrade note, but the options can not be removed. They must continue to exist.

@maximilianfridrich
Copy link
Contributor Author

This PR must be tested with the corresponding testsuite PR which adds 48 tests to test the codec negotiation:

Further, 10 tests were adopted to reflect the new behavior. For those tests, pjsip.conf had to be adopted to use the new ACN settings. The default settings prefer the pending stream while these tests expect the configured settings to be preferred.

@maximilianfridrich
Copy link
Contributor Author

the options can not be removed. They must continue to exist.

@jcolp And they must continue to work? Or silently be ignored like the ACN settings were? I'm not sure if I see a way for both settings to co-exist without interfering with each other.

@jcolp
Copy link
Member

jcolp commented Aug 30, 2023

Yes. We can not remove options in minor versions, or alter their behavior, unless there is a critical reason why - such as a security issue. This does not rise to that level.

@jcolp
Copy link
Member

jcolp commented Aug 30, 2023

For example, if the options are kept but default to empty in the code we can determine that they are actually unset. Default behavior should remain the same in that case even if the options were removed. If they are set then those settings take priority. If unset then the other options take priority.

  1. Default behavior with this change should match previous versions
  2. If the incoming_* and outgoing_* options are set by the user, then they take priority

@maximilianfridrich
Copy link
Contributor Author

We can not remove options in minor versions, or alter their behavior

Makes sense, I probably should have checked with the maintainers first. So would it be an option for Asterisk 21?

@jcolp
Copy link
Member

jcolp commented Aug 30, 2023

Asterisk 21, once branched, can no longer receive breaking changes.

@jcolp
Copy link
Member

jcolp commented Aug 30, 2023

I'm sticking to the policy that breaking changes are for standard releases, which next would be 23. If this change can be altered to maintain backwards compatibility then it would be eligible for all.

@maximilianfridrich
Copy link
Contributor Author

Ok, then I will look into your suggestion and try to find a way to not change the existing behavior and not remove any settings.

Is it possible to mark this PR as a draft while I do that?

@jcolp jcolp marked this pull request as draft August 30, 2023 09:58
@jcolp
Copy link
Member

jcolp commented Aug 30, 2023

I've converted it to a draft.

@maximilianfridrich
Copy link
Contributor Author

  1. Default behavior with this change should match previous versions
  2. If the incoming_* and outgoing_* options are set by the user, then they take priority

Consider the case where neither the "old" options incoming_call_offer_pref and outgoing_call_offer_pref nor the "new" codec_prefs_* options are set. As you described it, since the old options are unset, the new settings are used. And in order to not change the default behavior, I must change the defaults of two of the new ACN options:

codec_prefs_incoming_offer = prefer: configured, operation: intersect, keep: all, transcode: allow
codec_prefs_incoming_answer = prefer: configured, operation: intersect, keep: all, transcode: allow

They must be set to prefer: configured, this is the only way the requirements can be met. So far, the documentation (and code) had prefer: pending as default. The ACN settings had no effect so far, so I think this should be alright.

@gtjoseph
Copy link
Member

Thanks for picking this up!!!
I'm going to try and set some time aside to review in detail.

@maximilianfridrich
Copy link
Contributor Author

@gtjoseph Thank you! I am now incorporating Joshua's remarks and I noticed that some fax/pjsip tests failed (which I didn't check). So this is still a WIP, but we have confirmed that the ACN is working as expected (e.g. see the linked testsuite PR and we have done manual testing as well).

@maximilianfridrich
Copy link
Contributor Author

maximilianfridrich commented Aug 30, 2023

If the incoming_* and outgoing_* options are set by the user, then they take priority.

To be more precise, I think the logic should be:

As soon as one of incoming_call_offer_pref or outgoing_call_offer_pref is set, the ACN options are ignored.

I think everything else would lead to maximum confusion. I will state this in the docs for the settings.

@maximilianfridrich
Copy link
Contributor Author

Update: We can not really invest more time into making this patch also work with the old settings.

@jcolp Is the removal of the incoming_call_offer_pref and outgoing_call_offer_pref an option for the next Asterisk major version release? If so, how can we go about this? Should we close this PR and re-open it at a better time?

We are confident that this patch implements ACN as it is supposed to, the only missing piece seems to be the failing fax tests.

@InterLinked1
Copy link
Contributor

@jcolp Is the removal of the incoming_call_offer_pref and outgoing_call_offer_pref an option for the next Asterisk major version release? If so, how can we go about this? Should we close this PR and re-open it at a better time?

There is a waiting-for-standard-release-development-cycle label for this purpose, that could be applied. But I'm not sure contributors can add it themselves.

@jcolp jcolp added the waiting-for-standard-release-development-cycle This change is pending the master branch being the next standard release development cycle. label Aug 31, 2023
@jcolp
Copy link
Member

jcolp commented Aug 31, 2023

I've added it.

@gtjoseph gtjoseph force-pushed the master branch 4 times, most recently from c6b7d4b to 31abe63 Compare September 5, 2023 19:25
@btriller
Copy link
Contributor

This leaks some memory. Following patch fixes it. Unsure, if this is the correct approach though. @maximilianfridrich

diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 66ea75d0e0..e1d003180f 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -2917,6 +2917,10 @@ static int request(void *obj)
 		SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create session\n");
 	}
 
+	if (resolved_top) {
+		ast_stream_topology_free(resolved_top);
+	}
+
 	req_data->session = session;
 
 	SCOPE_EXIT_RTN_VALUE(0);
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 282878adb8..0da14eca13 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -941,6 +941,7 @@ static int handle_incoming_sdp(struct ast_sip_session *session, const pjmedia_sd
 					ast_trace(-1, "%s: No common codecs between incoming SDP offer and endpoint configuration.\n", ast_sip_session_get_name(session));
 				} else {
 					stream = ast_stream_clone(resolved_stream, stream_name);
+					ast_stream_free(resolved_stream);
 				}
 			} else {
 				/* This can happen when the stream is marked as removed.

@maximilianfridrich
Copy link
Contributor Author

maximilianfridrich commented Sep 12, 2023

Thank you for looking at it @btriller. That seems right. I declared both variables with the RAII_VAR macro now, that seems to be the best way. Not sure how this one slipped through and wasn't caught by our automated tests (which we ran with valgrind). I have updated the patch.

Also, please be aware that this patch as it is now breaks some of the fax/pjsip tests. The failing pjsip tests on GitHub are due to it not running it with the corresponding testsuite PR.

@btriller
Copy link
Contributor

There's another issue, that leads to no audio, if a client sends more streams than endpoint's capability. See #344 @maximilianfridrich

@maximilianfridrich
Copy link
Contributor Author

maximilianfridrich commented Sep 22, 2023

I'm not sure if I can follow. Could you please describe the scenario in more detail (endpoint config, and who answers/offers what)?

Update: Maybe all you need is operation: union in the correct codec_prefs_* setting.

@@ -5401,6 +5611,8 @@ static void session_inv_on_create_offer(pjsip_inv_session *inv, pjmedia_sdp_sess
SCOPE_EXIT_RTN("%s: create offer failed\n", ast_sip_session_get_name(session));
}

ast_sip_session_media_state_reset(session->pending_media_state);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to cause no audio with all_codecs_on_empty_reinvite=yes when a UA renegotiates a different codec. See [1] for reproducing this issue. Seems to be fixed with

diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 5486a97040..72b4130a40 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -5594,25 +5594,23 @@ static void session_inv_on_create_offer(pjsip_inv_session *inv, pjmedia_sdp_sess
 		}
 	}
 
-	if (inv->neg) {
-		if (pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
-			pjmedia_sdp_neg_get_active_remote(inv->neg, &previous_sdp);
-		} else {
-			pjmedia_sdp_neg_get_active_local(inv->neg, &previous_sdp);
-		}
-	}
-
 	if (ignore_active_stream_topology) {
 		offer = create_local_sdp(inv, session, NULL, 1);
 	} else {
+		if (inv->neg) {
+			if (pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
+				pjmedia_sdp_neg_get_active_remote(inv->neg, &previous_sdp);
+			} else {
+				pjmedia_sdp_neg_get_active_local(inv->neg, &previous_sdp);
+			}
+		}
 		offer = create_local_sdp(inv, session, previous_sdp, 0);
+		ast_sip_session_media_state_reset(session->pending_media_state);
 	}
 	if (!offer) {
 		SCOPE_EXIT_RTN("%s: create offer failed\n", ast_sip_session_get_name(session));
 	}
 
-	ast_sip_session_media_state_reset(session->pending_media_state);
-
 	ast_queue_unhold(session->channel);
 
 	/*

[1] acn_reinvite_without_sdp.patch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't doubt it, but the provided testcase passes for me even without your suggested patch. Is it testing exactly the scenario you described?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Negotiating codecs works fine without the patch but there's no audio (no RTP sent) after negotiating a new codec. I don't know how to verify that RTP is sent (maybe it's possible to check for RTP AMI events?). IIRC without the patch there's following log message

res_pjsip_sdp_rtp.c:487 set_caps: No joint capabilities for 'audio' media stream between our configuration((alaw)) and incoming SDP((g722))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, maybe I can find a way to test it. Thank you for clarifying.

@btriller
Copy link
Contributor

btriller commented Nov 8, 2023

There's another memory leak. Please check following patch. @maximilianfridrich

diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 5486a97040..cbbf6d05c4 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1225,8 +1225,8 @@ static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_
 		for (index = 0; index < local->media_count; index++) {
 			RAII_VAR(struct ast_format_cap *, remote_caps, NULL, ao2_cleanup);
 			RAII_VAR(struct ast_stream_topology *, remote_top, NULL, ao2_cleanup);
+			RAII_VAR(struct ast_stream *, resolved_stream, NULL, ast_stream_free);
 			struct ast_stream *remote_ast_stream;
-			struct ast_stream *resolved_stream;
 			struct ast_stream *configured_stream = ast_stream_topology_get_stream(session->pending_media_state->topology, index);
 			int skip_stream_setting = 0;
 

@btriller
Copy link
Contributor

btriller commented Nov 9, 2023

There's another memory leak. Please check following patch. @maximilianfridrich

diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 5486a97040..cbbf6d05c4 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1225,8 +1225,8 @@ static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_
 		for (index = 0; index < local->media_count; index++) {
 			RAII_VAR(struct ast_format_cap *, remote_caps, NULL, ao2_cleanup);
 			RAII_VAR(struct ast_stream_topology *, remote_top, NULL, ao2_cleanup);
+			RAII_VAR(struct ast_stream *, resolved_stream, NULL, ast_stream_free);
 			struct ast_stream *remote_ast_stream;
-			struct ast_stream *resolved_stream;
 			struct ast_stream *configured_stream = ast_stream_topology_get_stream(session->pending_media_state->topology, index);
 			int skip_stream_setting = 0;
 

Former patch leads to crashes. Following seams more appropriate

diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 5486a97040..9bfe47df30 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1259,6 +1259,7 @@ static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_
 				&session->endpoint->media.codec_prefs_incoming_answer,
 				NULL);
 			if (!resolved_stream || ast_format_cap_empty(ast_stream_get_formats(resolved_stream))) {
+				ast_stream_free(resolved_stream); /* Can handle NULL */
 				if (session->endpoint->media.codec_prefs_incoming_answer.transcode == CODEC_NEGOTIATION_TRANSCODE_ALLOW) {
 					ast_log(LOG_DEBUG, "%s: No common codecs between channels and transcoding allowed.\n",
 						ast_sip_session_get_name(session));

@maximilianfridrich
Copy link
Contributor Author

Former patch leads to crashes. Following seams more appropriate

That looks good, thank you for the find!

Advanced Codec Negotiation is now implemented for the dial application,
pjsip channels and local channels.

Resolves: asterisk#223

UpgradeNote: The two pjsip.conf options "incoming_call_offer_pref" and
"outgoing_call_offer_pref" have been removed. Instead, the new options
"codec_prefs_incoming_answer", "codec_prefs_incoming_offer",
"codec_prefs_outgoing_answer", "codec_prefs_outgoing_offer" must be
used. Even if the old settings were not used, this could break existing
deployments as the default ACN options are now in use and they might
behave differently in some call flows (e.g. by default, pending codecs
are always preferred, not the endpoint configuration).

UserNote: The Advanced Codec Negotiation feature is now implemented.
@SirDMZ
Copy link

SirDMZ commented Apr 9, 2024

Hey Maximilianfridrich,

Have you got a version of this patch for version 20 available?

@maximilianfridrich
Copy link
Contributor Author

maximilianfridrich commented Apr 9, 2024

@SirDMZ I tested the patches with Asterisk 20, they should work. A recent change (by me) has now created a conflict but it should be very easy to resolve.

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

Successfully merging this pull request may close these issues.

[new-feature]: Advanced Codec Negotiation
6 participants