; "0" or not enabled)
;contact_user= ; On outgoing requests, force the user portion of the Contact
; header to this value (default: "")
-;incoming_call_offer_pref= ; Sets the preferred codecs, and order to use between
- ; those received in the offer, and those set in this
- ; configuration's allow line. Valid values include:
+;incoming_call_offer_pref= ; Based on this setting, a joint list of
+ ; preferred codecs between those received in an
+ ; incoming SDP offer (remote), and those specified
+ ; in the endpoint's "allow" parameter (local)
+ ; is created and is passed to the Asterisk core.
;
- ; local - prefer and order by configuration (default).
- ; local_single - prefer and order by configuration,
- ; but only choose 'top' most codec
- ; remote - prefer and order by incoming sdp.
- ; remote_single - prefer and order by incoming sdp,
- ; but only choose 'top' most codec
-;preferred_codec_only=yes ; Respond to a SIP invite with the single most preferred codec
- ; rather than advertising all joint codec capabilities. This
- ; limits the other side's codec choice to exactly what we prefer.
- ; default is no.
+ ; local - Include all codecs in the local list that
+ ; are also in the remote list preserving the local
+ ; order. (default).
+ ; local_first - Include only the first codec in the
+ ; local list that is also in the remote list.
+ ; remote - Include all codecs in the remote list that
+ ; are also in the local list preserving remote list
+ ; order.
+ ; remote_first - Include only the first codec in
+ ; the remote list that is also in the local list.
+;outgoing_call_offer_pref= ; Based on this setting, a joint list of
+ ; preferred codecs between those received from the
+ ; Asterisk core (remote), and those specified in
+ ; the endpoint's "allow" parameter (local) is
+ ; created and is used to create the outgoing SDP
+ ; offer.
+ ;
+ ; local - Include all codecs in the local list that
+ ; are also in the remote list preserving the local
+ ; order.
+ ; local_merge - Include all codecs in BOTH lists
+ ; preserving the local list order. Codes in the
+ ; remote list not in the local list will be placed
+ ; at the end of the joint list.
+ ; local_first - Include only the first codec in the
+ ; local list.
+ ; remote - Include all codecs in the remote list that
+ ; are also in the local list preserving remote list
+ ; order. (default)
+ ; remote_merge - Include all codecs in BOTH lists
+ ; preserving the remote list order. Codes in the
+ ; local list not in the remote list will be placed
+ ; at the end of the joint list.
+ ; remote_first - Include only the first codec in
+ ; the remote list.
+;preferred_codec_only=no ; Respond to a SIP invite with the single most
+ ; preferred codec rather than advertising all joint
+ ; codec capabilities. This limits the other side's
+ ; codec choice to exactly what we prefer.
+ ; default is no.
+ ; NOTE: This option is deprecated in favor
+ ; of incoming_call_offer_pref. Setting both
+ ; options is unsupported.
;asymmetric_rtp_codec= ; Allow the sending and receiving codec to differ and
; not be automatically matched (default: "no")
;refer_blind_progress= ; Whether to notifies all the progress details on blind
--- /dev/null
+Subject: res_pjsip
+Subject: res_pjsip_session
+Master-Only: True
+
+Two new options, incoming_call_offer_pref and outgoing_call_offer_pref
+have been added to res_pjsip endpoints that specify the preferred order
+of codecs to use between those received/sent in an SDP offer and those
+set in the endpoint configuration.
+++ /dev/null
-Subject: res_pjsip
-Subject: res_pjsip_session
-Master-Only: True
-
-A new option, incoming_call_offer_pref, was added to res_pjsip endpoints that
-specifies the preferred order of codecs to use between those received in the
-offer, and those set in the configuration.
-
-Valid values include:
- local - prefer and order by configuration (default).
- local_single - prefer and order by configuration, but only choose 'top'
- most codec
- remote - prefer and order by incoming sdp.
- remote_single - prefer and order by incoming sdp, but only choose 'top' most
- most codec
-
-Example A:
- [alice]
- type=endpoint
- incoming_call_offer_pref=local
- allow=!all,opus,alaw,ulaw
-
- Alice's incoming sdp=g722,ulaw,alaw
- RESULT: alaw,ulaw
-
-Example B:
- [alice]
- type=endpoint
- incoming_call_offer_pref=local_single
- allow=!all,opus,alaw,ulaw
-
- Alice's incoming sdp=g722,ulaw,alaw
- RESULT: alaw
-
-Example C:
- [alice]
- type=endpoint
- incoming_call_offer_pref=remote
- allow=!all,opus,alaw,ulaw
-
- Alice's incoming sdp=g722,ulaw,alaw
- RESULT: ulaw,alaw
-
-Example D:
- [alice]
- type=endpoint
- incoming_call_offer_pref=remote_single
- allow=!all,opus,alaw,ulaw
-
- Alice's incoming sdp=g722,ulaw,alaw
- RESULT: ulaw
-
-
/*!
* \brief Incoming/Outgoing call offer/answer joint codec preference.
+ *
+ * The default is INTERSECT ALL LOCAL.
*/
enum ast_sip_call_codec_pref {
+ /*! Two bits for merge */
+ /*! Intersection of local and remote */
+ AST_SIP_CALL_CODEC_PREF_INTERSECT = 1 << 0,
+ /*! Union of local and remote */
+ AST_SIP_CALL_CODEC_PREF_UNION = 1 << 1,
+
+ /*! Two bits for filter */
+ /*! No filter */
+ AST_SIP_CALL_CODEC_PREF_ALL = 1 << 2,
+ /*! Only the first */
+ AST_SIP_CALL_CODEC_PREF_FIRST = 1 << 3,
+
+ /*! Two bits for preference and sort */
/*! Prefer, and order by local values */
- AST_SIP_CALL_CODEC_PREF_LOCAL,
- /*! Prefer, and order by local values (intersection) */
- AST_SIP_CALL_CODEC_PREF_LOCAL_LIMIT,
- /*! Prefer, and order by local values (top/first only) */
- AST_SIP_CALL_CODEC_PREF_LOCAL_SINGLE,
+ AST_SIP_CALL_CODEC_PREF_LOCAL = 1 << 4,
/*! Prefer, and order by remote values */
- AST_SIP_CALL_CODEC_PREF_REMOTE,
- /*! Prefer, and order by remote values (intersection) */
- AST_SIP_CALL_CODEC_PREF_REMOTE_LIMIT,
- /*! Prefer, and order by remote values (top/first only) */
- AST_SIP_CALL_CODEC_PREF_REMOTE_SINGLE,
+ AST_SIP_CALL_CODEC_PREF_REMOTE = 1 << 5,
};
+/*!
+ * \brief Returns true if the preference is set in the parameter
+ * \since 18.0.0
+ *
+ * \param param A ast_flags struct with one or more of enum ast_sip_call_codec_pref set
+ * \param codec_pref The last component of one of the enum values
+ * \retval 1 if the enum value is set
+ * \retval 0 if not
+ */
+#define ast_sip_call_codec_pref_test(__param, __codec_pref) (!!(ast_test_flag( &__param, AST_SIP_CALL_CODEC_PREF_ ## __codec_pref )))
+
/*!
* \brief Session timers options
*/
/*! Enable webrtc settings and defaults */
unsigned int webrtc;
/*! Codec preference for an incoming offer */
- enum ast_sip_call_codec_pref incoming_call_offer_pref;
+ struct ast_flags incoming_call_offer_pref;
+ /*! Codec preference for an outgoing offer */
+ struct ast_flags outgoing_call_offer_pref;
};
/*!
*/
int ast_sip_str_to_dtmf(const char *dtmf_mode);
+/*!
+ * \brief Convert the call codec preference flags to a string
+ * \since 18.0.0
+ *
+ * \param pref the call codec preference setting
+ *
+ * \returns a constant string with either the setting value or 'unknown'
+ * \note Don't try to free the string!
+ *
+ */
+const char *ast_sip_call_codec_pref_to_str(struct ast_flags pref);
+
/*!
* \brief Transport shutdown monitor callback.
* \since 13.18.0
#include "asterisk/sdp_srtp.h"
/* Needed for ast_media_type */
#include "asterisk/codec.h"
+/* Needed for pjmedia_sdp_session and pjsip_inv_session */
+#include <pjsip_ua.h>
+
/* Forward declarations */
struct ast_sip_endpoint;
struct ast_sip_session_sdp_handler *handler;
/*! \brief Holds SRTP information */
struct ast_sdp_srtp *srtp;
- /*! \brief Media format capabilities */
- struct ast_sip_session_caps *caps;
/*! \brief What type of encryption is in use on this stream */
enum ast_sip_session_media_encryption encryption;
/*! \brief The media transport in use for this stream */
/*! \brief Opaque struct controlling the suspension of the session's serializer. */
struct ast_sip_session_suspender;
+/*! \brief Indicates the call direction respective to Asterisk */
+enum ast_sip_session_call_direction {
+ AST_SIP_SESSION_INCOMING_CALL = 0,
+ AST_SIP_SESSION_OUTGOING_CALL,
+};
+
/*!
* \brief A structure describing a SIP session
*
enum ast_sip_dtmf_mode dtmf;
/*! Initial incoming INVITE Request-URI. NULL otherwise. */
pjsip_uri *request_uri;
- /* Media statistics for negotiated RTP streams */
+ /*! Media statistics for negotiated RTP streams */
AST_VECTOR(, struct ast_rtp_instance_stats *) media_stats;
+ /*! The direction of the call respective to Asterisk */
+ enum ast_sip_session_call_direction call_direction;
};
typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata);
struct ast_format_cap;
struct ast_sip_session;
-struct ast_sip_session_media;
-struct ast_sip_session_caps;
/*!
- * \brief Allocate a SIP session capabilities object.
+ * \brief Create joint capabilities
* \since 18.0.0
*
- * \retval An ao2 allocated SIP session capabilities object, or NULL on error
- */
-struct ast_sip_session_caps *ast_sip_session_caps_alloc(void);
-
-/*!
- * \brief Set the incoming call offer capabilities for a session.
- * \since 18.0.0
+ * Creates a list of joint capabilities between the given remote capabilities, and local ones.
+ * "local" and "remote" reference the values in ast_sip_call_codec_pref.
*
- * This will replace any capabilities already present.
+ * \param remote The "remote" capabilities
+ * \param local The "local" capabilities
+ * \param media_type The media type
+ * \param codec_prefs One or more of enum ast_sip_call_codec_pref
*
- * \param caps A session's capabilities object
- * \param cap The capabilities to set it to
+ * \retval A pointer to the joint capabilities (which may be empty).
+ * NULL will be returned only if no memory was available to allocate the structure.
+ * \note Returned object's reference must be released at some point,
*/
-void ast_sip_session_set_incoming_call_offer_cap(struct ast_sip_session_caps *caps,
- struct ast_format_cap *cap);
+struct ast_format_cap *ast_sip_create_joint_call_cap(const struct ast_format_cap *remote,
+ struct ast_format_cap *local, enum ast_media_type media_type,
+ struct ast_flags codec_pref);
/*!
- * \brief Get the incoming call offer capabilities.
+ * \brief Create a new stream of joint capabilities
* \since 18.0.0
*
- * \note Returned objects reference is not incremented.
+ * Creates a new stream with capabilities between the given session's local capabilities,
+ * and the remote stream's. Codec selection is based on the session->endpoint's codecs, the
+ * session->endpoint's codec call preferences, and the stream passed by the core (for
+ * outgoing calls) or created by the incoming SDP (for incoming calls).
*
- * \param caps A session's capabilities object
+ * \param session The session
+ * \param remote The remote stream
*
- * \retval An incoming call offer capabilities object
+ * \retval A pointer to a new stream with the joint capabilities (which may be empty),
+ * NULL will be returned only if no memory was available to allocate the structure.
*/
-const struct ast_format_cap *ast_sip_session_get_incoming_call_offer_cap(
- const struct ast_sip_session_caps *caps);
+struct ast_stream *ast_sip_session_create_joint_call_stream(const struct ast_sip_session *session,
+ struct ast_stream *remote);
/*!
- * \brief Make the incoming call offer capabilities for a session.
+ * \brief Create joint capabilities
* \since 18.0.0
*
- * Creates and sets a list of joint capabilities between the given remote
- * capabilities, and pre-configured ones. The resulting joint list is then
- * stored, and 'owned' (reference held) by the session.
- *
- * If the incoming capabilities have been set elsewhere, this will not replace
- * those. It will however, return a pointer to the current set.
- *
- * \note Returned object's reference is not incremented.
+ * Creates a list of joint capabilities between the given session's local capabilities,
+ * and the remote capabilities. Codec selection is based on the session->endpoint's codecs, the
+ * session->endpoint's codec call preferences, and the "remote" capabilities passed by the core (for
+ * outgoing calls) or created by the incoming SDP (for incoming calls).
*
* \param session The session
- * \param session_media An associated media session
- * \param remote Capabilities of a device
+ * \param media_type The media type
+ * \param remote Capabilities received in an SDP offer or from the core
*
- * \retval A pointer to the incoming call offer capabilities
+ * \retval A pointer to the joint capabilities (which may be empty).
+ * NULL will be returned only if no memory was available to allocate the structure.
+ * \note Returned object's reference must be released at some point,
*/
-const struct ast_format_cap *ast_sip_session_join_incoming_call_offer_cap(
- const struct ast_sip_session *session, const struct ast_sip_session_media *session_media,
- const struct ast_format_cap *remote);
+struct ast_format_cap *ast_sip_session_create_joint_call_cap(const struct ast_sip_session *session,
+ enum ast_media_type media_type, const struct ast_format_cap *remote);
#endif /* RES_PJSIP_SESSION_CAPS_H */
*/
struct ast_format_cap *ast_stream_get_formats(const struct ast_stream *stream);
+/*!
+ * \brief Get the count of the current negotiated formats of a stream
+ *
+ * \param stream The media stream
+ *
+ * \return The count of negotiated formats
+ *
+ * \since 18
+ */
+int ast_stream_get_format_count(const struct ast_stream *stream);
+
/*!
* \brief Set the current negotiated formats of a stream
*
return stream->formats;
}
+int ast_stream_get_format_count(const struct ast_stream *stream)
+{
+ ast_assert(stream != NULL);
+
+ return stream->formats ? ast_format_cap_count(stream->formats) : 0;
+}
+
void ast_stream_set_formats(struct ast_stream *stream, struct ast_format_cap *caps)
{
ast_assert(stream != NULL);
</para></description>
</configOption>
<configOption name="preferred_codec_only" default="no">
- <synopsis>Respond to a SIP invite with the single most preferred codec rather than advertising all joint codec capabilities. This limits the other side's codec choice to exactly what we prefer.</synopsis>
+ <synopsis>Respond to a SIP invite with the single most preferred codec (DEPRECATED)</synopsis>
+ <description><para>Respond to a SIP invite with the single most preferred codec
+ rather than advertising all joint codec capabilities. This limits the other side's codec
+ choice to exactly what we prefer.</para>
+ <warning><para>This option has been deprecated in favor of
+ <literal>incoming_call_offer_pref</literal>. Setting both options is unsupported.</para>
+ </warning>
+ </description>
+ <see-also>
+ <ref type="configOption">incoming_call_offer_pref</ref>
+ </see-also>
</configOption>
<configOption name="incoming_call_offer_pref" default="local">
- <synopsis>After receiving an incoming offer create a list of preferred codecs between
- those received in the SDP offer, and those specified in endpoint configuration.</synopsis>
+ <synopsis>Preferences for selecting codecs for an incoming call.</synopsis>
+ <description>
+ <para>Based on this setting, a joint list of preferred codecs between those
+ received in an incoming SDP offer (remote), and those specified in the
+ endpoint's "allow" parameter (local) es created and is passed to the Asterisk
+ core. </para>
+ <note><para>This list will consist of only those codecs found in both lists.</para></note>
+ <enumlist>
+ <enum name="local"><para>
+ Include all codecs in the local list that are also in the remote list
+ preserving the local order. (default).
+ </para></enum>
+ <enum name="local_first"><para>
+ Include only the first codec in the local list that is also in the remote list.
+ </para></enum>
+ <enum name="remote"><para>
+ Include all codecs in the remote list that are also in the local list
+ preserving the remote order.
+ </para></enum>
+ <enum name="remote_first"><para>
+ Include only the first codec in the remote list that is also in the local list.
+ </para></enum>
+ </enumlist>
+ </description>
+ </configOption>
+ <configOption name="outgoing_call_offer_pref" default="local">
+ <synopsis>Preferences for selecting codecs for an outgoing call.</synopsis>
<description>
- <note><para>This list will consist of only those codecs found in both.</para></note>
+ <para>Based on this setting, a joint list of preferred codecs between
+ those received from the Asterisk core (remote), and those specified in
+ the endpoint's "allow" parameter (local) is created and is used to create
+ the outgoing SDP offer.</para>
<enumlist>
<enum name="local"><para>
- Order by the endpoint configuration allow line (default)
+ Include all codecs in the local list that are also in the remote list
+ preserving the local order.
</para></enum>
- <enum name="local_single"><para>
- Order by the endpoint configuration allow line, but the list will only contain the first, or 'top' item
+ <enum name="local_merge"><para>
+ Include all codecs in BOTH lists preserving the local order.
+ Remote codecs not in the local list will be placed at the end
+ of the joint list.
+ </para></enum>
+ <enum name="local_first"><para>
+ Include only the first codec in the local list.
</para></enum>
<enum name="remote"><para>
- Order by what is received in the SDP offer
+ Include all codecs in the remote list that are also in the local list
+ preserving the remote order. (default)
+ </para></enum>
+ <enum name="remote_merge"><para>
+ Include all codecs in BOTH lists preserving the remote order.
+ Local codecs not in the remote list will be placed at the end
+ of the joint list.
</para></enum>
- <enum name="remote_single"><para>
- Order by what is received in the SDP offer, but the list will only contain the first, or 'top' item
+ <enum name="remote_first"><para>
+ Include only the first codec in the remote list.
</para></enum>
</enumlist>
</description>
return result;
}
+const char *ast_sip_call_codec_pref_to_str(struct ast_flags pref)
+{
+ const char *value;
+
+ if (ast_sip_call_codec_pref_test(pref, LOCAL) && ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, ALL)) {
+ value = "local";
+ } else if (ast_sip_call_codec_pref_test(pref, LOCAL) && ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, ALL)) {
+ value = "local_merge";
+ } else if (ast_sip_call_codec_pref_test(pref, LOCAL) && ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, FIRST)) {
+ value = "local_first";
+ } else if (ast_sip_call_codec_pref_test(pref, REMOTE) && ast_sip_call_codec_pref_test(pref, INTERSECT) && ast_sip_call_codec_pref_test(pref, ALL)) {
+ value = "remote";
+ } else if (ast_sip_call_codec_pref_test(pref, REMOTE) && ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, ALL)) {
+ value = "remote_merge";
+ } else if (ast_sip_call_codec_pref_test(pref, REMOTE) && ast_sip_call_codec_pref_test(pref, UNION) && ast_sip_call_codec_pref_test(pref, FIRST)) {
+ value = "remote_first";
+ } else {
+ value = "unknown";
+ }
+
+ return value;
+}
+
/*!
* \brief Set name and number information on an identity header.
*
return 0;
}
-static const char *sip_call_codec_pref_strings[] = {
- [AST_SIP_CALL_CODEC_PREF_LOCAL] = "local",
- [AST_SIP_CALL_CODEC_PREF_LOCAL_LIMIT] = "local_limit",
- [AST_SIP_CALL_CODEC_PREF_LOCAL_SINGLE] = "local_single",
- [AST_SIP_CALL_CODEC_PREF_REMOTE] = "remote",
- [AST_SIP_CALL_CODEC_PREF_REMOTE_LIMIT] = "remote_limit",
- [AST_SIP_CALL_CODEC_PREF_REMOTE_SINGLE] = "remote_single",
-};
-
-static int incoming_call_offer_pref_handler(const struct aco_option *opt,
+static int call_offer_pref_handler(const struct aco_option *opt,
struct ast_variable *var, void *obj)
{
struct ast_sip_endpoint *endpoint = obj;
- unsigned int i;
-
- for (i = 0; i < ARRAY_LEN(sip_call_codec_pref_strings); ++i) {
- if (!strcmp(var->value, sip_call_codec_pref_strings[i])) {
- /* Local and remote limit are not available values for this option */
- if (i == AST_SIP_CALL_CODEC_PREF_LOCAL_LIMIT ||
- i == AST_SIP_CALL_CODEC_PREF_REMOTE_LIMIT) {
- return -1;
- }
+ struct ast_flags pref = { 0, };
+ int outgoing = strcmp(var->name, "outgoing_call_offer_pref") == 0;
+
+ if (strcmp(var->value, "local") == 0) {
+ ast_set_flag(&pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL);
+ } else if (outgoing && strcmp(var->value, "local_merge") == 0) {
+ ast_set_flag(&pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_ALL);
+ } else if (strcmp(var->value, "local_first") == 0) {
+ ast_set_flag(&pref, AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_FIRST);
+ } else if (strcmp(var->value, "remote") == 0) {
+ ast_set_flag(&pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL);
+ } else if (outgoing && strcmp(var->value, "remote_merge") == 0) {
+ ast_set_flag(&pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_ALL);
+ } else if (strcmp(var->value, "remote_first") == 0) {
+ ast_set_flag(&pref, AST_SIP_CALL_CODEC_PREF_REMOTE | AST_SIP_CALL_CODEC_PREF_UNION | AST_SIP_CALL_CODEC_PREF_FIRST);
+ } else {
+ return -1;
+ }
- endpoint->media.incoming_call_offer_pref = i;
- return 0;
- }
+ if (outgoing) {
+ endpoint->media.outgoing_call_offer_pref = pref;
+ } else {
+ endpoint->media.incoming_call_offer_pref = pref;
}
- return -1;
+ return 0;
}
static int incoming_call_offer_pref_to_str(const void *obj, const intptr_t *args, char **buf)
{
const struct ast_sip_endpoint *endpoint = obj;
- if (ARRAY_IN_BOUNDS(endpoint->media.incoming_call_offer_pref, sip_call_codec_pref_strings)) {
- *buf = ast_strdup(sip_call_codec_pref_strings[endpoint->media.incoming_call_offer_pref]);
+ *buf = ast_strdup(ast_sip_call_codec_pref_to_str(endpoint->media.incoming_call_offer_pref));
+ if (!(*buf)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int outgoing_call_offer_pref_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+ const struct ast_sip_endpoint *endpoint = obj;
+
+ *buf = ast_strdup(ast_sip_call_codec_pref_to_str(endpoint->media.outgoing_call_offer_pref));
+ if (!(*buf)) {
+ return -1;
}
return 0;
return -1;
}
+ if (endpoint->preferred_codec_only) {
+ if (endpoint->media.incoming_call_offer_pref.flags != (AST_SIP_CALL_CODEC_PREF_LOCAL | AST_SIP_CALL_CODEC_PREF_INTERSECT | AST_SIP_CALL_CODEC_PREF_ALL)) {
+ ast_log(LOG_ERROR, "Setting both preferred_codec_only and incoming_call_offer_pref is not supported on endpoint '%s'\n",
+ ast_sorcery_object_get_id(endpoint));
+ return -1;
+ }
+ ast_clear_flag(&endpoint->media.incoming_call_offer_pref, AST_SIP_CALL_CODEC_PREF_ALL);
+ ast_set_flag(&endpoint->media.incoming_call_offer_pref, AST_SIP_CALL_CODEC_PREF_FIRST);
+ }
+
endpoint->media.topology = ast_stream_topology_create_from_format_cap(endpoint->media.codecs);
if (!endpoint->media.topology) {
return -1;
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "suppress_q850_reason_headers", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, suppress_q850_reason_headers));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ignore_183_without_sdp", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, ignore_183_without_sdp));
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "incoming_call_offer_pref", "local",
- incoming_call_offer_pref_handler, incoming_call_offer_pref_to_str, NULL, 0, 0);
+ call_offer_pref_handler, incoming_call_offer_pref_to_str, NULL, 0, 0);
+ ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outgoing_call_offer_pref", "remote",
+ call_offer_pref_handler, outgoing_call_offer_pref_to_str, NULL, 0, 0);
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
static int apply_cap_to_bundled(struct ast_sip_session_media *session_media,
struct ast_sip_session_media *session_media_transport,
- struct ast_stream *asterisk_stream, const struct ast_format_cap *joint)
+ struct ast_stream *asterisk_stream, struct ast_format_cap *joint)
{
if (!joint) {
return -1;
}
- ast_stream_set_formats(asterisk_stream, (struct ast_format_cap *)joint);
+ ast_stream_set_formats(asterisk_stream, joint);
/* If this is a bundled stream then apply the payloads to RTP instance acting as transport to prevent conflicts */
if (session_media_transport != session_media && session_media->bundled) {
return 0;
}
-static const struct ast_format_cap *set_incoming_call_offer_cap(
+static struct ast_format_cap *set_incoming_call_offer_cap(
struct ast_sip_session *session, struct ast_sip_session_media *session_media,
const struct pjmedia_sdp_media *stream)
{
- const struct ast_format_cap *incoming_call_offer_cap;
+ struct ast_format_cap *incoming_call_offer_cap;
struct ast_format_cap *remote;
struct ast_rtp_codecs codecs = AST_RTP_CODECS_NULL_INIT;
int fmts = 0;
get_codecs(session, stream, &codecs, session_media);
ast_rtp_codecs_payload_formats(&codecs, remote, &fmts);
- incoming_call_offer_cap = ast_sip_session_join_incoming_call_offer_cap(
- session, session_media, remote);
+ incoming_call_offer_cap = ast_sip_session_create_joint_call_cap(
+ session, session_media->type, remote);
ao2_ref(remote, -1);
- if (!incoming_call_offer_cap) {
+ if (!incoming_call_offer_cap || ast_format_cap_empty(incoming_call_offer_cap)) {
+ ao2_cleanup(incoming_call_offer_cap);
ast_rtp_codecs_payloads_destroy(&codecs);
return NULL;
}
struct ast_sip_session_media *session_media_transport;
enum ast_media_type media_type = session_media->type;
enum ast_sip_session_media_encryption encryption = AST_SIP_MEDIA_ENCRYPT_NONE;
+ struct ast_format_cap *joint;
int res;
/* If no type formats have been configured reject this stream */
}
}
- if (apply_cap_to_bundled(session_media, session_media_transport, asterisk_stream,
- set_incoming_call_offer_cap(session, session_media, stream))) {
+ joint = set_incoming_call_offer_cap(session, session_media, stream);
+ res = apply_cap_to_bundled(session_media, session_media_transport, asterisk_stream, joint);
+ ao2_cleanup(joint);
+ if (res != 0) {
return 0;
}
ast_free(session_media->mid);
ast_free(session_media->remote_mslabel);
-
- ao2_cleanup(session_media->caps);
}
struct ast_sip_session_media *ast_sip_session_media_state_add(struct ast_sip_session *session,
} else {
session_media->bundle_group = -1;
}
-
- session_media->caps = ast_sip_session_caps_alloc();
- if (!session_media->caps) {
- ao2_ref(session_media, -1);
- return NULL;
- }
}
if (AST_VECTOR_REPLACE(&media_state->sessions, position, session_media)) {
return NULL;
}
session->aor = ao2_bump(found_aor);
+ session->call_direction = AST_SIP_SESSION_OUTGOING_CALL;
+
ast_party_id_copy(&session->id, &endpoint->id.self);
if (ast_stream_topology_get_count(req_topology) > 0) {
for (i = 0; i < ast_stream_topology_get_count(req_topology); ++i) {
struct ast_stream *req_stream;
- struct ast_format_cap *req_cap;
- struct ast_format_cap *joint_cap;
struct ast_stream *clone_stream;
req_stream = ast_stream_topology_get_stream(req_topology, i);
continue;
}
- req_cap = ast_stream_get_formats(req_stream);
-
- joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- if (!joint_cap) {
- continue;
- }
-
- ast_format_cap_get_compatible(req_cap, endpoint->media.codecs, joint_cap);
-
- if (ast_stream_get_type(req_stream) == AST_MEDIA_TYPE_AUDIO) {
- /*
- * By appending codecs from the endpoint after compatible ones this
- * guarantees that priority is given to those while also allowing
- * translation to occur for non-compatible.
- */
- ast_format_cap_append_from_cap(joint_cap,
- endpoint->media.codecs, AST_MEDIA_TYPE_AUDIO);
- }
-
- if (!ast_format_cap_count(joint_cap)) {
- ao2_ref(joint_cap, -1);
- continue;
- }
-
- clone_stream = ast_stream_clone(req_stream, NULL);
- if (!clone_stream) {
- ao2_ref(joint_cap, -1);
+ clone_stream = ast_sip_session_create_joint_call_stream(session, req_stream);
+ if (!clone_stream || ast_stream_get_format_count(clone_stream) == 0) {
+ ast_stream_free(clone_stream);
continue;
}
- ast_stream_set_formats(clone_stream, joint_cap);
- ao2_ref(joint_cap, -1);
-
if (!session->pending_media_state->topology) {
session->pending_media_state->topology = ast_stream_topology_alloc();
if (!session->pending_media_state->topology) {
#endif
return;
}
+ session->call_direction = AST_SIP_SESSION_INCOMING_CALL;
/*
* The current thread is supposed be the session serializer to prevent
#include "asterisk/format_cap.h"
#include "asterisk/logger.h"
#include "asterisk/sorcery.h"
-
-#include <pjsip_ua.h>
-
+#include "asterisk/stream.h"
#include "asterisk/res_pjsip.h"
#include "asterisk/res_pjsip_session.h"
#include "asterisk/res_pjsip_session_caps.h"
-struct ast_sip_session_caps {
- struct ast_format_cap *incoming_call_offer_cap;
-};
-
static void log_caps(int level, const char *file, int line, const char *function,
- const char *msg, const struct ast_sip_session *session,
- const struct ast_sip_session_media *session_media, const struct ast_format_cap *local,
- const struct ast_format_cap *remote, const struct ast_format_cap *joint)
+ const struct ast_sip_session *session, enum ast_media_type media_type,
+ const struct ast_format_cap *local, const struct ast_format_cap *remote,
+ const struct ast_format_cap *joint)
{
struct ast_str *s1;
struct ast_str *s2;
struct ast_str *s3;
+ int outgoing = session->call_direction == AST_SIP_SESSION_OUTGOING_CALL;
+ struct ast_flags pref =
+ outgoing
+ ? session->endpoint->media.outgoing_call_offer_pref
+ : session->endpoint->media.incoming_call_offer_pref;
if (level == __LOG_DEBUG && !DEBUG_ATLEAST(3)) {
return;
s2 = remote ? ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN) : NULL;
s3 = joint ? ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN) : NULL;
- ast_log(level, file, line, function, "'%s' %s '%s' capabilities -%s%s%s%s%s%s\n",
+ ast_log(level, file, line, function, "'%s' Caps for %s %s call with pref '%s' - remote: %s local: %s joint: %s\n",
session->channel ? ast_channel_name(session->channel) :
ast_sorcery_object_get_id(session->endpoint),
- msg ? msg : "-", ast_codec_media_type2str(session_media->type),
- s1 ? " local: " : "", s1 ? ast_format_cap_get_names(local, &s1) : "",
- s2 ? " remote: " : "", s2 ? ast_format_cap_get_names(remote, &s2) : "",
- s3 ? " joint: " : "", s3 ? ast_format_cap_get_names(joint, &s3) : "");
+ outgoing? "outgoing" : "incoming",
+ ast_codec_media_type2str(media_type),
+ ast_sip_call_codec_pref_to_str(pref),
+ s2 ? ast_format_cap_get_names(remote, &s2) : "(NONE)",
+ s1 ? ast_format_cap_get_names(local, &s1) : "(NONE)",
+ s3 ? ast_format_cap_get_names(joint, &s3) : "(NONE)");
}
-static void sip_session_caps_destroy(void *obj)
+struct ast_format_cap *ast_sip_create_joint_call_cap(const struct ast_format_cap *remote,
+ struct ast_format_cap *local, enum ast_media_type media_type,
+ struct ast_flags codec_pref)
{
- struct ast_sip_session_caps *caps = obj;
-
- ao2_cleanup(caps->incoming_call_offer_cap);
-}
-
-struct ast_sip_session_caps *ast_sip_session_caps_alloc(void)
-{
- return ao2_alloc_options(sizeof(struct ast_sip_session_caps),
- sip_session_caps_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
-}
-
-void ast_sip_session_set_incoming_call_offer_cap(struct ast_sip_session_caps *caps,
- struct ast_format_cap *cap)
-{
- ao2_cleanup(caps->incoming_call_offer_cap);
- caps->incoming_call_offer_cap = ao2_bump(cap);
-}
-
-const struct ast_format_cap *ast_sip_session_get_incoming_call_offer_cap(
- const struct ast_sip_session_caps *caps)
-{
- return caps->incoming_call_offer_cap;
-}
-
-const struct ast_format_cap *ast_sip_session_join_incoming_call_offer_cap(
- const struct ast_sip_session *session, const struct ast_sip_session_media *session_media,
- const struct ast_format_cap *remote)
-{
- enum ast_sip_call_codec_pref pref;
- struct ast_format_cap *joint;
- struct ast_format_cap *local;
-
- joint = session_media->caps->incoming_call_offer_cap;
-
- if (joint) {
- /*
- * If the incoming call offer capabilities have been set elsewhere, e.g. dialplan
- * then those take precedence.
- */
- return joint;
- }
-
- joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
- local = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
-
- if (!joint || !local) {
- ast_log(LOG_ERROR, "Failed to allocate %s incoming call offer capabilities\n",
- ast_codec_media_type2str(session_media->type));
+ struct ast_format_cap *joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+ struct ast_format_cap *local_filtered = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+ if (!joint || !local_filtered) {
+ ast_log(LOG_ERROR, "Failed to allocate %s call offer capabilities\n",
+ ast_codec_media_type2str(media_type));
ao2_cleanup(joint);
- ao2_cleanup(local);
+ ao2_cleanup(local_filtered);
return NULL;
}
- pref = session->endpoint->media.incoming_call_offer_pref;
- ast_format_cap_append_from_cap(local, session->endpoint->media.codecs,
- session_media->type);
+ ast_format_cap_append_from_cap(local_filtered, local, media_type);
- if (pref < AST_SIP_CALL_CODEC_PREF_REMOTE) {
- ast_format_cap_get_compatible(local, remote, joint); /* Prefer local */
+ if (ast_sip_call_codec_pref_test(codec_pref, LOCAL)) {
+ if (ast_sip_call_codec_pref_test(codec_pref, INTERSECT)) {
+ ast_format_cap_get_compatible(local_filtered, remote, joint); /* Get common, prefer local */
+ } else {
+ ast_format_cap_append_from_cap(joint, local_filtered, media_type); /* Add local */
+ ast_format_cap_append_from_cap(joint, remote, media_type); /* Then remote */
+ }
} else {
- ast_format_cap_get_compatible(remote, local, joint); /* Prefer remote */
+ if (ast_sip_call_codec_pref_test(codec_pref, INTERSECT)) {
+ ast_format_cap_get_compatible(remote, local_filtered, joint); /* Get common, prefer remote */
+ } else {
+ ast_format_cap_append_from_cap(joint, remote, media_type); /* Add remote */
+ ast_format_cap_append_from_cap(joint, local_filtered, media_type); /* Then local */
+ }
}
- if (ast_format_cap_empty(joint)) {
- log_caps(LOG_NOTICE, "No joint incoming", session, session_media, local, remote, NULL);
+ ao2_ref(local_filtered, -1);
- ao2_ref(joint, -1);
- ao2_ref(local, -1);
- return NULL;
+ if (ast_format_cap_empty(joint)) {
+ return joint;
}
- if (pref == AST_SIP_CALL_CODEC_PREF_LOCAL_SINGLE ||
- pref == AST_SIP_CALL_CODEC_PREF_REMOTE_SINGLE ||
- session->endpoint->preferred_codec_only) {
-
+ if (ast_sip_call_codec_pref_test(codec_pref, FIRST)) {
/*
* Save the most preferred one. Session capabilities are per stream and
* a stream only carries a single media type, so no reason to worry with
ao2_ref(single, -1);
}
- log_caps(LOG_DEBUG, "Joint incoming", session, session_media, local, remote, joint);
+ return joint;
+}
+
+struct ast_stream *ast_sip_session_create_joint_call_stream(const struct ast_sip_session *session,
+ struct ast_stream *remote_stream)
+{
+ struct ast_stream *joint_stream = ast_stream_clone(remote_stream, NULL);
+ struct ast_format_cap *remote = ast_stream_get_formats(remote_stream);
+ enum ast_media_type media_type = ast_stream_get_type(remote_stream);
+
+ struct ast_format_cap *joint = ast_sip_create_joint_call_cap(remote,
+ session->endpoint->media.codecs, media_type,
+ session->call_direction == AST_SIP_SESSION_OUTGOING_CALL
+ ? session->endpoint->media.outgoing_call_offer_pref
+ : session->endpoint->media.incoming_call_offer_pref);
+
+ ast_stream_set_formats(joint_stream, joint);
+ ao2_cleanup(joint);
- ao2_ref(local, -1);
+ log_caps(LOG_DEBUG, session, media_type, session->endpoint->media.codecs, remote, joint);
+
+ return joint_stream;
+}
+
+struct ast_format_cap *ast_sip_session_create_joint_call_cap(
+ const struct ast_sip_session *session, enum ast_media_type media_type,
+ const struct ast_format_cap *remote)
+{
+ struct ast_format_cap *joint = ast_sip_create_joint_call_cap(remote,
+ session->endpoint->media.codecs, media_type,
+ session->call_direction == AST_SIP_SESSION_OUTGOING_CALL
+ ? session->endpoint->media.outgoing_call_offer_pref
+ : session->endpoint->media.incoming_call_offer_pref);
- ast_sip_session_set_incoming_call_offer_cap(session_media->caps, joint);
+ log_caps(LOG_DEBUG, session, media_type, session->endpoint->media.codecs, remote, joint);
return joint;
}