]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
sdp: Add support for setting connection address and clean up state.
authorJoshua Colp <jcolp@digium.com>
Mon, 27 Mar 2017 20:32:45 +0000 (20:32 +0000)
committerJoshua Colp <jcolp@digium.com>
Thu, 30 Mar 2017 18:26:10 +0000 (18:26 +0000)
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

include/asterisk/sdp.h
include/asterisk/sdp_options.h
include/asterisk/sdp_state.h
main/sdp.c
main/sdp_options.c
main/sdp_state.c

index 4d6d2fbb5d5d239144698c64f390ad3d4662cdb4..3649b40377846c57d0b3bbe04417576784793295 100644 (file)
@@ -395,20 +395,20 @@ struct ast_sdp_a_line *ast_sdp_get_a(const struct ast_sdp *sdp, int index);
 int ast_sdp_add_m(struct ast_sdp *sdp, struct ast_sdp_m_line *m_line);
 
 /*!
- * \brief Add a Media Description to an SDP
+ * \brief Add an RTP Media Description to an SDP
  *
  * \param sdp SDP
+ * \param sdp_state SDP state information
  * \param options SDP Options
- * \param rtp ast_rtp_instance
- * \param stream stream
+ * \param stream_index stream
  *
  * \retval 0 Success
  * \retval non-0 Failure
  *
  * \since 15
  */
-int ast_sdp_add_m_from_stream(struct ast_sdp *sdp, const struct ast_sdp_options *options,
-       struct ast_rtp_instance *rtp, const struct ast_stream *stream);
+int ast_sdp_add_m_from_rtp_stream(struct ast_sdp *sdp, const struct ast_sdp_state *sdp_state,
+       const struct ast_sdp_options *options, int stream_index);
 
 /*!
  * \brief Get the count of Media Descriptions on an SDP
index 3995faf4a9e69e16cd09d2b54a4581b866ad542d..0186eea57c83f1553aa84b2201c743073e1e8ac6 100644 (file)
@@ -268,26 +268,6 @@ void ast_sdp_options_set_g726_non_standard(struct ast_sdp_options *options,
  */
 unsigned int ast_sdp_options_get_g726_non_standard(struct ast_sdp_options *options);
 
-/*!
- * \since 15.0.0
- * \brief Set SDP Options locally_held
- *
- * \param options SDP Options
- * \param locally_held
- */
-void ast_sdp_options_set_locally_held(struct ast_sdp_options *options,
-       unsigned int locally_held);
-
-/*!
- * \since 15.0.0
- * \brief Get SDP Options locally_held
- *
- * \param options SDP Options
- *
- * \returns locally_held
- */
-unsigned int ast_sdp_options_get_locally_held(struct ast_sdp_options *options);
-
 /*!
  * \since 15.0.0
  * \brief Set SDP Options tos_audio
index e2f13eb61355ae67a9bf08950d4bd645a472b150..a186d7eefecc30de9c3e143c031742a55d01ab3c 100644 (file)
@@ -23,6 +23,7 @@
 #include "asterisk/sdp_options.h"
 
 struct ast_sdp_state;
+struct ast_sockaddr;
 
 /*!
  * \brief Allocate a new SDP state
@@ -50,6 +51,26 @@ void ast_sdp_state_free(struct ast_sdp_state *sdp_state);
 struct ast_rtp_instance *ast_sdp_state_get_rtp_instance(const struct ast_sdp_state *sdp_state,
        int stream_index);
 
+/*!
+ * \brief Get the global connection address on the SDP state.
+ */
+const struct ast_sockaddr *ast_sdp_state_get_connection_address(const struct ast_sdp_state *sdp_state);
+
+/*!
+ * \brief Get the connection address for a particular stream.
+ *
+ * \param sdp_state
+ * \param stream_index The particular stream to get the connection address of
+ * \param address[out] A place to store the address in
+ *
+ * \retval 0 Success
+ *
+ * \note
+ * Stream numbers correspond to the streams in the topology of the associated channel
+ */
+int ast_sdp_state_get_stream_connection_address(const struct ast_sdp_state *sdp_state,
+       int stream_index, struct ast_sockaddr *address);
+
 /*!
  * \brief Get the joint negotiated streams based on local and remote capabilities.
  *
@@ -149,4 +170,69 @@ int ast_sdp_state_set_remote_sdp_from_impl(struct ast_sdp_state *sdp_state, void
  */
 int ast_sdp_state_reset(struct ast_sdp_state *sdp_state);
 
+/*!
+ * \brief Update the local stream topology on the SDP state.
+ *
+ * \param sdp_state
+ * \param streams The new stream topology.
+ *
+ * \retval 0 Success
+ *
+ * \since 15
+ */
+int ast_sdp_state_update_local_topology(struct ast_sdp_state *sdp_state, struct ast_stream_topology *streams);
+
+/*!
+ * \brief Set the local address (IP address) to use for connection addresses
+ *
+ * \param sdp_state
+ * \param address The local address
+ *
+ * \note
+ * Passing NULL as an address will unset the explicit local connection address.
+ *
+ * \since 15
+ */
+void ast_sdp_state_set_local_address(struct ast_sdp_state *sdp_state, struct ast_sockaddr *address);
+
+/*!
+ * \brief Set the connection address (IP address and port) to use for a specific stream
+ *
+ * \param sdp_state
+ * \param stream_index The stream to set the connection address for
+ * \param address The connection address
+ *
+ * \retval 0 Success
+ *
+ * \note
+ * Passing NULL as an address will unset the explicit local connection address.
+ *
+ * \since 15
+ */
+int ast_sdp_state_set_connection_address(struct ast_sdp_state *sdp_state, int stream_index,
+       struct ast_sockaddr *address);
+
+/*!
+ * \since 15.0.0
+ * \brief Set a stream to be held or unheld
+ *
+ * \param sdp_state
+ * \param stream_index The stream to set the held value for
+ * \param locally_held
+ */
+void ast_sdp_state_set_locally_held(struct ast_sdp_state *sdp_state,
+       int stream_index, unsigned int locally_held);
+
+/*!
+ * \since 15.0.0
+ * \brief Get whether a stream is held or not
+ *
+ * \param sdp_state
+ * \param stream_index The stream to get the held state for
+ *
+ * \returns locally_held
+ */
+unsigned int ast_sdp_state_get_locally_held(const struct ast_sdp_state *sdp_state,
+       int stream_index);
+
 #endif /* _ASTERISK_SDP_STATE_H */
index 246763edf492a0c46627e77d18203844ae64c204..1ef6400b9197ab445c978352ba8f4a64ef10e390 100644 (file)
@@ -500,69 +500,10 @@ int ast_sdp_m_add_format(struct ast_sdp_m_line *m_line, const struct ast_sdp_opt
        return 0;
 }
 
-/* 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;
-}
-
-int ast_sdp_add_m_from_stream(struct ast_sdp *sdp, const struct ast_sdp_options *options,
-       struct ast_rtp_instance *rtp, const struct ast_stream *stream)
+int ast_sdp_add_m_from_rtp_stream(struct ast_sdp *sdp, const struct ast_sdp_state *sdp_state,
+       const struct ast_sdp_options *options, int stream_index)
 {
+       struct ast_stream *stream = ast_stream_topology_get_stream(ast_sdp_state_get_local_topology(sdp_state), stream_index);
        struct ast_sdp_m_line *m_line;
        struct ast_format_cap *caps;
        int i;
@@ -572,13 +513,15 @@ int ast_sdp_add_m_from_stream(struct ast_sdp *sdp, const struct ast_sdp_options
        enum ast_media_type media_type;
        char tmp[64];
        struct ast_sockaddr address_rtp;
+       struct ast_rtp_instance *rtp = ast_sdp_state_get_rtp_instance(sdp_state, stream_index);
        struct ast_sdp_a_line *a_line;
 
-
-       ast_assert(sdp && options && rtp && stream);
+       ast_assert(sdp && options && stream);
 
        media_type = ast_stream_get_type(stream);
-       ast_rtp_instance_get_local_address(rtp, &address_rtp);
+       if (ast_sdp_state_get_stream_connection_address(sdp_state, 0, &address_rtp)) {
+               return -1;
+       }
 
        m_line = ast_sdp_m_alloc(
                ast_codec_media_type2str(ast_stream_get_type(stream)),
@@ -673,7 +616,7 @@ int ast_sdp_add_m_from_stream(struct ast_sdp *sdp, const struct ast_sdp_options
                }
        }
 
-       a_line = ast_sdp_a_alloc(options->locally_held ? "sendonly" : "sendrecv", "");
+       a_line = ast_sdp_a_alloc(ast_sdp_state_get_locally_held(sdp_state, stream_index) ? "sendonly" : "sendrecv", "");
        if (!a_line || ast_sdp_m_add_a(m_line, a_line)) {
                ast_sdp_a_free(a_line);
                ast_sdp_m_free(m_line);
@@ -699,7 +642,6 @@ struct ast_sdp *ast_sdp_create_from_state(const struct ast_sdp_state *sdp_state)
        struct ast_sdp_c_line *c_line = NULL;
        struct ast_sdp_s_line *s_line = NULL;
        struct ast_sdp_t_line *t_line = NULL;
-       struct ast_rtp_instance *rtp = NULL;
        char *address_type;
        struct timeval tv = ast_tvnow();
        uint32_t t;
@@ -732,25 +674,18 @@ struct ast_sdp *ast_sdp_create_from_state(const struct ast_sdp_state *sdp_state)
        }
 
        for (stream_num = 0; stream_num < stream_count; stream_num++) {
-               struct ast_stream *stream = ast_stream_topology_get_stream(topology, stream_num);
-
-               rtp = create_rtp(options, ast_stream_get_type(stream));
-               if (!rtp) {
-                       goto error;
-               }
-
-               ast_stream_set_data(stream, AST_STREAM_DATA_RTP_INSTANCE,
-                       rtp, (ast_stream_data_free_fn)&ast_rtp_instance_destroy);
+               enum ast_media_type type = ast_stream_get_type(ast_stream_topology_get_stream(topology, stream_num));
 
-               if (ast_sdp_add_m_from_stream(sdp, options, rtp, stream)) {
-                       goto error;
+               if (type == AST_MEDIA_TYPE_AUDIO || type == AST_MEDIA_TYPE_VIDEO) {
+                       if (ast_sdp_add_m_from_rtp_stream(sdp, sdp_state, options, stream_num)) {
+                               goto error;
+                       }
                }
        }
 
        return sdp;
 
 error:
-       ao2_cleanup(rtp);
        if (sdp) {
                ast_sdp_free(sdp);
        } else {
index ca076ac7b7d7e00655059b8d592e63c1eb5626a3..60848172218b5cdde58a5f7bdccd9540948e8591 100644 (file)
@@ -64,7 +64,6 @@ DEFINE_GETTERS_SETTERS_FOR(unsigned int, rtp_symmetric);
 DEFINE_GETTERS_SETTERS_FOR(unsigned int, telephone_event);
 DEFINE_GETTERS_SETTERS_FOR(unsigned int, rtp_ipv6);
 DEFINE_GETTERS_SETTERS_FOR(unsigned int, g726_non_standard);
-DEFINE_GETTERS_SETTERS_FOR(unsigned int, locally_held);
 DEFINE_GETTERS_SETTERS_FOR(unsigned int, tos_audio);
 DEFINE_GETTERS_SETTERS_FOR(unsigned int, cos_audio);
 DEFINE_GETTERS_SETTERS_FOR(unsigned int, tos_video);
index 1b09ce16f15a8fe1dea4e8560374b8dd855fcc00..5858a65ab06f2f44146e75dbbcb04393e539c9de 100644 (file)
 #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;
+}