summaryrefslogtreecommitdiff
path: root/main/sdp_state.c
diff options
context:
space:
mode:
authorJoshua Colp <jcolp@digium.com>2017-03-27 20:32:45 +0000
committerJoshua Colp <jcolp@digium.com>2017-03-30 18:26:10 +0000
commitf3290d6b664bd2b6ed840b7501666e26b341acb1 (patch)
tree7a796814363551c54ee07364265102b9a7b47243 /main/sdp_state.c
parent3bdf876b045653c427df0a7a771e90dd15fa1527 (diff)
sdp: Add support for setting connection address and clean up state.
This change cleans up state management for media streams by moving RTP instances into their own session structure and adding additional details that are not relevant to the core (such as connection address). These can live either in the local capabilities or joint capabilities. The ability to set explicit connection address information for the purposes of direct media and NAT has also been added at the global and stream specific level. ASTERISK-26900 Change-Id: If7e5307239a9534420732de11c451a2705b6b681
Diffstat (limited to 'main/sdp_state.c')
-rw-r--r--main/sdp_state.c302
1 files changed, 287 insertions, 15 deletions
diff --git a/main/sdp_state.c b/main/sdp_state.c
index 1b09ce16f..5858a65ab 100644
--- a/main/sdp_state.c
+++ b/main/sdp_state.c
@@ -22,10 +22,14 @@
#include "asterisk/sdp_translator.h"
#include "asterisk/vector.h"
#include "asterisk/utils.h"
+#include "asterisk/netsock2.h"
+#include "asterisk/rtp_engine.h"
#include "../include/asterisk/sdp.h"
#include "asterisk/stream.h"
+#include "sdp_private.h"
+
enum ast_sdp_state_machine {
/*! \brief The initial state.
*
@@ -61,13 +65,33 @@ enum ast_sdp_state_machine {
typedef int (*state_fn)(struct ast_sdp_state *state);
+struct sdp_state_stream {
+ union {
+ /*! The underlying RTP instance */
+ struct ast_rtp_instance *instance;
+ };
+ /*! An explicit connection address for this stream */
+ struct ast_sockaddr connection_address;
+ /*! Whether this stream is held or not */
+ unsigned int locally_held;
+};
+
+struct sdp_state_capabilities {
+ /*! Stream topology */
+ struct ast_stream_topology *topology;
+ /*! Additional information about the streams */
+ AST_VECTOR(, struct sdp_state_stream) streams;
+ /*! An explicit global connection address */
+ struct ast_sockaddr connection_address;
+};
+
struct ast_sdp_state {
/*! Local capabilities, learned through configuration */
- struct ast_stream_topology *local_capabilities;
+ struct sdp_state_capabilities local_capabilities;
/*! Remote capabilities, learned through remote SDP */
struct ast_stream_topology *remote_capabilities;
/*! Joint capabilities. The combined local and remote capabilities. */
- struct ast_stream_topology *joint_capabilities;
+ struct sdp_state_capabilities joint_capabilities;
/*! Local SDP. Generated via the options and local capabilities. */
struct ast_sdp *local_sdp;
/*! Remote SDP. Received directly from a peer. */
@@ -100,25 +124,42 @@ struct ast_sdp_state *ast_sdp_state_alloc(struct ast_stream_topology *streams,
return NULL;
}
- sdp_state->local_capabilities = ast_stream_topology_clone(streams);
- if (!sdp_state->local_capabilities) {
+ if (ast_sdp_state_update_local_topology(sdp_state, streams)) {
ast_sdp_state_free(sdp_state);
return NULL;
}
+
sdp_state->state = SDP_STATE_INITIAL;
return sdp_state;
}
+static void sdp_state_capabilities_free(struct sdp_state_capabilities *sdp_capabilities)
+{
+ int stream_index;
+
+ for (stream_index = 0; stream_index < AST_VECTOR_SIZE(&sdp_capabilities->streams); stream_index++) {
+ struct sdp_state_stream *stream_state = AST_VECTOR_GET_ADDR(&sdp_capabilities->streams, stream_index);
+ enum ast_media_type type = ast_stream_get_type(ast_stream_topology_get_stream(sdp_capabilities->topology, stream_index));
+
+ if (type == AST_MEDIA_TYPE_AUDIO || type == AST_MEDIA_TYPE_VIDEO) {
+ ast_rtp_instance_destroy(stream_state->instance);
+ }
+ }
+
+ ast_stream_topology_free(sdp_capabilities->topology);
+ AST_VECTOR_FREE(&sdp_capabilities->streams);
+}
+
void ast_sdp_state_free(struct ast_sdp_state *sdp_state)
{
if (!sdp_state) {
return;
}
- ast_stream_topology_free(sdp_state->local_capabilities);
+ sdp_state_capabilities_free(&sdp_state->local_capabilities);
ast_stream_topology_free(sdp_state->remote_capabilities);
- ast_stream_topology_free(sdp_state->joint_capabilities);
+ sdp_state_capabilities_free(&sdp_state->joint_capabilities);
ast_sdp_free(sdp_state->local_sdp);
ast_sdp_free(sdp_state->remote_sdp);
ast_sdp_free(sdp_state->joint_sdp);
@@ -126,19 +167,75 @@ void ast_sdp_state_free(struct ast_sdp_state *sdp_state)
ast_sdp_translator_free(sdp_state->translator);
}
+static struct sdp_state_stream *sdp_state_get_stream(const struct ast_sdp_state *sdp_state, int stream_index)
+{
+ if (stream_index >= AST_VECTOR_SIZE(&sdp_state->local_capabilities.streams)) {
+ return NULL;
+ }
+
+ return AST_VECTOR_GET_ADDR(&sdp_state->local_capabilities.streams, stream_index);
+}
+
struct ast_rtp_instance *ast_sdp_state_get_rtp_instance(
const struct ast_sdp_state *sdp_state, int stream_index)
{
- struct ast_stream *stream;
+ struct sdp_state_stream *stream_state;
ast_assert(sdp_state != NULL);
- stream = ast_stream_topology_get_stream(sdp_state->local_capabilities, stream_index);
- if (!stream) {
+ stream_state = sdp_state_get_stream(sdp_state, stream_index);
+ if (!stream_state) {
return NULL;
}
- return (struct ast_rtp_instance *)ast_stream_get_data(stream, AST_STREAM_DATA_RTP_INSTANCE);
+ return stream_state->instance;
+}
+
+const struct ast_sockaddr *ast_sdp_state_get_connection_address(const struct ast_sdp_state *sdp_state)
+{
+ ast_assert(sdp_state != NULL);
+
+ return &sdp_state->local_capabilities.connection_address;
+}
+
+int ast_sdp_state_get_stream_connection_address(const struct ast_sdp_state *sdp_state,
+ int stream_index, struct ast_sockaddr *address)
+{
+ struct sdp_state_stream *stream_state;
+ enum ast_media_type type;
+
+ ast_assert(sdp_state != NULL);
+ ast_assert(address != NULL);
+
+ stream_state = sdp_state_get_stream(sdp_state, stream_index);
+ if (!stream_state) {
+ return -1;
+ }
+
+ /* If an explicit connection address has been provided for the stream return it */
+ if (!ast_sockaddr_isnull(&stream_state->connection_address)) {
+ ast_sockaddr_copy(address, &stream_state->connection_address);
+ return 0;
+ }
+
+ type = ast_stream_get_type(ast_stream_topology_get_stream(sdp_state->local_capabilities.topology,
+ stream_index));
+
+ if (type == AST_MEDIA_TYPE_AUDIO || type == AST_MEDIA_TYPE_VIDEO) {
+ ast_rtp_instance_get_local_address(stream_state->instance, address);
+ } else {
+ return -1;
+ }
+
+ /* If an explicit global connection address is set use it here for the IP part */
+ if (!ast_sockaddr_isnull(&sdp_state->local_capabilities.connection_address)) {
+ int port = ast_sockaddr_port(address);
+
+ ast_sockaddr_copy(address, &sdp_state->local_capabilities.connection_address);
+ ast_sockaddr_set_port(address, port);
+ }
+
+ return 0;
}
const struct ast_stream_topology *ast_sdp_state_get_joint_topology(
@@ -146,9 +243,9 @@ const struct ast_stream_topology *ast_sdp_state_get_joint_topology(
{
ast_assert(sdp_state != NULL);
if (sdp_state->state == SDP_STATE_NEGOTIATED) {
- return sdp_state->joint_capabilities;
+ return sdp_state->joint_capabilities.topology;
} else {
- return sdp_state->local_capabilities;
+ return sdp_state->local_capabilities.topology;
}
}
@@ -157,7 +254,7 @@ const struct ast_stream_topology *ast_sdp_state_get_local_topology(
{
ast_assert(sdp_state != NULL);
- return sdp_state->local_capabilities;
+ return sdp_state->local_capabilities.topology;
}
const struct ast_sdp_options *ast_sdp_state_get_options(
@@ -193,11 +290,109 @@ static int merge_sdps(struct ast_sdp_state *sdp_state)
}
#endif
+/* TODO
+ * This isn't set anywhere yet.
+ */
+/*! \brief Scheduler for RTCP purposes */
+static struct ast_sched_context *sched;
+
+/*! \brief Internal function which creates an RTP instance */
+static struct ast_rtp_instance *create_rtp(const struct ast_sdp_options *options,
+ enum ast_media_type media_type)
+{
+ struct ast_rtp_instance *rtp;
+ struct ast_rtp_engine_ice *ice;
+ struct ast_sockaddr temp_media_address;
+ static struct ast_sockaddr address_rtp;
+ struct ast_sockaddr *media_address = &address_rtp;
+
+ if (options->bind_rtp_to_media_address && !ast_strlen_zero(options->media_address)) {
+ ast_sockaddr_parse(&temp_media_address, options->media_address, 0);
+ media_address = &temp_media_address;
+ } else {
+ if (ast_check_ipv6()) {
+ ast_sockaddr_parse(&address_rtp, "::", 0);
+ } else {
+ ast_sockaddr_parse(&address_rtp, "0.0.0.0", 0);
+ }
+ }
+
+ if (!(rtp = ast_rtp_instance_new(options->rtp_engine, sched, media_address, NULL))) {
+ ast_log(LOG_ERROR, "Unable to create RTP instance using RTP engine '%s'\n",
+ options->rtp_engine);
+ return NULL;
+ }
+
+ ast_rtp_instance_set_prop(rtp, AST_RTP_PROPERTY_RTCP, 1);
+ ast_rtp_instance_set_prop(rtp, AST_RTP_PROPERTY_NAT, options->rtp_symmetric);
+
+ if (options->ice == AST_SDP_ICE_DISABLED && (ice = ast_rtp_instance_get_ice(rtp))) {
+ ice->stop(rtp);
+ }
+
+ if (options->telephone_event) {
+ ast_rtp_instance_dtmf_mode_set(rtp, AST_RTP_DTMF_MODE_RFC2833);
+ ast_rtp_instance_set_prop(rtp, AST_RTP_PROPERTY_DTMF, 1);
+ }
+
+ if (media_type == AST_MEDIA_TYPE_AUDIO &&
+ (options->tos_audio || options->cos_audio)) {
+ ast_rtp_instance_set_qos(rtp, options->tos_audio,
+ options->cos_audio, "SIP RTP Audio");
+ } else if (media_type == AST_MEDIA_TYPE_VIDEO &&
+ (options->tos_video || options->cos_video)) {
+ ast_rtp_instance_set_qos(rtp, options->tos_video,
+ options->cos_video, "SIP RTP Video");
+ }
+
+ ast_rtp_instance_set_last_rx(rtp, time(NULL));
+
+ return rtp;
+}
+
+static int sdp_state_setup_local_streams(struct ast_sdp_state *sdp_state)
+{
+ int stream_index;
+
+ for (stream_index = 0; stream_index < AST_VECTOR_SIZE(&sdp_state->local_capabilities.streams); stream_index++) {
+ struct sdp_state_stream *stream_state_local = AST_VECTOR_GET_ADDR(&sdp_state->local_capabilities.streams, stream_index);
+ struct sdp_state_stream *stream_state_joint = NULL;
+ enum ast_media_type type_local = ast_stream_get_type(ast_stream_topology_get_stream(sdp_state->local_capabilities.topology, stream_index));
+ enum ast_media_type type_joint = AST_MEDIA_TYPE_UNKNOWN;
+
+ if (stream_index < AST_VECTOR_SIZE(&sdp_state->joint_capabilities.streams)) {
+ stream_state_joint = AST_VECTOR_GET_ADDR(&sdp_state->joint_capabilities.streams, stream_index);
+ type_joint = ast_stream_get_type(ast_stream_topology_get_stream(sdp_state->joint_capabilities.topology, stream_index));
+ }
+
+ /* If we can reuse an existing media stream then do so */
+ if (type_local == type_joint) {
+ if (type_local == AST_MEDIA_TYPE_AUDIO || type_local == AST_MEDIA_TYPE_VIDEO) {
+ stream_state_local->instance = ao2_bump(stream_state_joint->instance);
+ continue;
+ }
+ }
+
+ if (type_local == AST_MEDIA_TYPE_AUDIO || type_local == AST_MEDIA_TYPE_VIDEO) {
+ /* We need to create a new RTP instance */
+ stream_state_local->instance = create_rtp(sdp_state->options, type_local);
+ if (!stream_state_local->instance) {
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
const struct ast_sdp *ast_sdp_state_get_local_sdp(struct ast_sdp_state *sdp_state)
{
ast_assert(sdp_state != NULL);
if (!sdp_state->local_sdp) {
+ if (sdp_state_setup_local_streams(sdp_state)) {
+ return NULL;
+ }
sdp_state->local_sdp = ast_sdp_create_from_state(sdp_state);
}
@@ -254,10 +449,87 @@ int ast_sdp_state_reset(struct ast_sdp_state *sdp_state)
ast_stream_topology_free(sdp_state->remote_capabilities);
sdp_state->remote_capabilities = NULL;
- ast_stream_topology_free(sdp_state->joint_capabilities);
- sdp_state->joint_capabilities = NULL;
+ ast_stream_topology_free(sdp_state->joint_capabilities.topology);
+ sdp_state->joint_capabilities.topology = NULL;
sdp_state->state = SDP_STATE_INITIAL;
return 0;
}
+
+int ast_sdp_state_update_local_topology(struct ast_sdp_state *sdp_state, struct ast_stream_topology *streams)
+{
+ ast_assert(sdp_state != NULL);
+ ast_assert(streams != NULL);
+
+ sdp_state_capabilities_free(&sdp_state->local_capabilities);
+ sdp_state->local_capabilities.topology = ast_stream_topology_clone(streams);
+ if (!sdp_state->local_capabilities.topology) {
+ return -1;
+ }
+
+ if (AST_VECTOR_INIT(&sdp_state->local_capabilities.streams, ast_stream_topology_get_count(streams))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+void ast_sdp_state_set_local_address(struct ast_sdp_state *sdp_state, struct ast_sockaddr *address)
+{
+ ast_assert(sdp_state != NULL);
+
+ if (!address) {
+ ast_sockaddr_setnull(&sdp_state->local_capabilities.connection_address);
+ } else {
+ ast_sockaddr_copy(&sdp_state->local_capabilities.connection_address, address);
+ }
+}
+
+int ast_sdp_state_set_connection_address(struct ast_sdp_state *sdp_state, int stream_index,
+ struct ast_sockaddr *address)
+{
+ struct sdp_state_stream *stream_state;
+ ast_assert(sdp_state != NULL);
+
+ stream_state = sdp_state_get_stream(sdp_state, stream_index);
+ if (!stream_state) {
+ return -1;
+ }
+
+ if (!address) {
+ ast_sockaddr_setnull(&stream_state->connection_address);
+ } else {
+ ast_sockaddr_copy(&stream_state->connection_address, address);
+ }
+
+ return 0;
+}
+
+void ast_sdp_state_set_locally_held(struct ast_sdp_state *sdp_state,
+ int stream_index, unsigned int locally_held)
+{
+ struct sdp_state_stream *stream_state;
+ ast_assert(sdp_state != NULL);
+
+ stream_state = sdp_state_get_stream(sdp_state, stream_index);
+ if (!stream_state) {
+ return;
+ }
+
+ stream_state->locally_held = locally_held;
+}
+
+unsigned int ast_sdp_state_get_locally_held(const struct ast_sdp_state *sdp_state,
+ int stream_index)
+{
+ struct sdp_state_stream *stream_state;
+ ast_assert(sdp_state != NULL);
+
+ stream_state = sdp_state_get_stream(sdp_state, stream_index);
+ if (!stream_state) {
+ return 0;
+ }
+
+ return stream_state->locally_held;
+}