[Asterisk-code-review] SDP: Add initial SDP state machine. (asterisk[master])

Mark Michelson asteriskteam at digium.com
Wed Feb 22 17:37:42 CST 2017


Mark Michelson has uploaded a new change for review. ( https://gerrit.asterisk.org/5072 )

Change subject: SDP: Add initial SDP state machine.
......................................................................

SDP: Add initial SDP state machine.

This introduces and documents the various states in the state machine.
This also introduces API functions that induce state changes, and places
TODO comments telling what needs to be done in addition to what is
already there. Those TODOs will be replaced with real code in upcoming
changes.

Change-Id: I871c0eb480b4c84d83e91ac5628e7a673e8b89ed
---
M include/asterisk/sdp_state.h
M main/sdp_state.c
2 files changed, 195 insertions(+), 1 deletion(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/72/5072/1

diff --git a/include/asterisk/sdp_state.h b/include/asterisk/sdp_state.h
index b5e4417..1ea7549 100644
--- a/include/asterisk/sdp_state.h
+++ b/include/asterisk/sdp_state.h
@@ -55,4 +55,43 @@
  */
 struct ast_stream_topology *ast_sdp_state_get_joint_topology(struct ast_sdp_state *sdp_state);
 
+/*!
+ * \brief Get the local SDP.
+ *
+ * If we have not received a remote SDP yet, this will be an SDP offer based
+ * on known streams and options If we have received a remote SDP, this will
+ * be the negotiated SDP based on the joint capabilities. The return type is
+ * a void pointer because the representation of the SDP is going to be determined based
+ * on the SDP options when allocating the SDP state.
+ *
+ * This function will allocate RTP instances if RTP instances have not already
+ * been allocated for the streams.
+ *
+ * The return here is const. The use case for this is so that a channel can add the SDP to an outgoing
+ * message. The API user should not attempt to modify the SDP. SDP modification should only be done through
+ * the API.
+ */
+void *ast_sdp_state_get_local(struct ast_sdp_state *sdp_state);
+
+/*!
+ * \brief Set the remote SDP.
+ *
+ * This can be used for either a remote offer or answer.
+ * This can also be used whenever an UPDATE, re-INVITE, etc. arrives.
+ * The type of the "remote" parameter is dictated by whatever SDP representation
+ * was set in the ast_sdp_options used during ast_sdp_state allocation
+ *
+ * This function will NOT allocate RTP instances.
+ */
+int ast_sdp_state_set_remote(struct ast_sdp_state *sdp_state, void *remote);
+
+/*!
+ * \brief Reset the SDP state and stream capabilities as if the SDP state had just been allocated.
+ *
+ * This is most useful for when a channel driver is sending a session refresh message
+ * and needs to re-advertise its initial capabilities instead of the previously-negotiated
+ * joint capabilities.
+ */
+int ast_sdp_state_reset(struct ast_sdp_state *sdp_state);
+
 #endif /* _ASTERISK_SDP_STATE_H */
diff --git a/main/sdp_state.c b/main/sdp_state.c
index 04de6e3..bcf4a8b 100644
--- a/main/sdp_state.c
+++ b/main/sdp_state.c
@@ -25,6 +25,41 @@
 #include "asterisk/utils.h"
 #include "asterisk/stream.h"
 
+enum ast_sdp_state_machine {
+	/*! \brief The initial state.
+	 *
+	 * The state machine starts here. It also goes back to this
+	 * state whenever ast_sdp_state_reset() is called.
+	 */
+	SDP_STATE_INITIAL,
+	/*! \brief We are the SDP offerer.
+	 *
+	 * The state machine enters this state if in the initial state
+	 * and ast_sdp_state_get_local() is called. When this state is
+	 * entered, a local SDP is created and then returned.
+	 */
+	SDP_STATE_OFFERER,
+	/*! \brief We are the SDP answerer.
+	 *
+	 * The state machine enters this state if in the initial state
+	 * and ast_sdp_state_set_remote() is called.
+	 */
+	SDP_STATE_ANSWERER,
+	/*! \brief The SDP has been negotiated.
+	 *
+	 * This state can be entered from either the offerer or answerer
+	 * state. When this state is entered, a joint SDP is created.
+	 */
+	SDP_STATE_NEGOTIATED,
+	/*! \brief Not an actual state.
+	 *
+	 * This is just here to mark the end of the enumeration.
+	 */
+	SDP_STATE_END,
+};
+
+typedef int (*state_fn)(struct ast_sdp_state *state);
+
 struct ast_sdp_state {
 	/*! Local capabilities, learned through configuration */
 	struct ast_stream_topology *local_capabilities;
@@ -44,6 +79,8 @@
 	struct ast_sdp_translator *translator;
 	/*! RTP instance for each media stream */
 	AST_VECTOR(, struct ast_rtp_instance *) rtp;
+	/*! The current state machine state that we are in */
+	enum ast_sdp_state_machine state;
 };
 
 struct ast_sdp_state *ast_sdp_state_alloc(struct ast_stream_topology *streams, struct ast_sdp_options *options)
@@ -68,6 +105,7 @@
 		ast_sdp_state_free(sdp_state);
 		return NULL;
 	}
+	sdp_state->state = SDP_STATE_INITIAL;
 
 	return sdp_state;
 }
@@ -90,6 +128,8 @@
 
 struct ast_rtp_instance *ast_sdp_state_get_rtp_instance(struct ast_sdp_state *sdp_state, int stream_index)
 {
+	ast_assert(sdp_state != NULL);
+
 	if (stream_index >= AST_VECTOR_SIZE(&sdp_state->rtp)) {
 		return NULL;
 	}
@@ -99,9 +139,124 @@
 
 struct ast_stream_topology *ast_sdp_state_get_joint_topology(struct ast_sdp_state *sdp_state)
 {
-	if (sdp_state->joint_capabilities) {
+	ast_assert(sdp_state != NULL);
+	if (sdp_state->state == SDP_STATE_NEGOTIATED) {
 		return sdp_state->joint_capabilities;
 	} else {
 		return sdp_state->local_capabilities;
 	}
 }
+
+static int merge_sdps(struct ast_sdp_state *sdp_state)
+{
+	ast_assert(sdp_state->local_sdp != NULL);
+	ast_assert(sdp_state->remote_sdp != NULL);
+	/* XXX STUB */
+	/* The goal of this function is to take
+	 * sdp_state->local_sdp and sdp_state->remote_sdp
+	 * and negotiate those into a joint SDP. This joint
+	 * SDP should be stored in sdp_state->joint_sdp. After
+	 * the joint SDP is created, the joint SDP should be
+	 * used to create the joint topology. Finally, if necessary,
+	 * the RTP session may need to be adjusted in some ways. For
+	 * instance, if we previously opened three ports for three
+	 * streams, but we negotiate down to two streams, then we
+	 * can shut down the port for the third stream. Similarly,
+	 * if we end up negotiating something like BUNDLE, then we may
+	 * need to tell the RTP layer to close ports and to multiplex
+	 * streams.
+	 */
+
+	return 0;
+}
+
+void *ast_sdp_state_get_local(struct ast_sdp_state *sdp_state)
+{
+	struct ast_sdp *sdp;
+
+	ast_assert(sdp_state != NULL);
+
+	/*TODO Create RTP instances based on local topology and SDP options (if not already created) */
+	/*TODO Create local SDP based on local topology, SDP options, and RTP ports (if not already created) */
+
+	switch (sdp_state->state) {
+	case SDP_STATE_INITIAL:
+		sdp_state->state = SDP_STATE_OFFERER;
+		/* Fall through */
+	case SDP_STATE_OFFERER:
+	default:
+		sdp = sdp_state->local_sdp;
+		break;
+	case SDP_STATE_ANSWERER:
+		sdp_state->state = SDP_STATE_NEGOTIATED;
+		merge_sdps(sdp_state);
+		/* Fall through */
+	case SDP_STATE_NEGOTIATED:
+		sdp = sdp_state->joint_sdp;
+		break;
+	}
+
+	return ast_sdp_translator_from_sdp(sdp_state->translator, sdp);
+}
+
+int ast_sdp_state_set_remote(struct ast_sdp_state *sdp_state, void *remote)
+{
+	struct ast_sdp *sdp;
+
+	ast_assert(sdp_state != NULL);
+
+	sdp = ast_sdp_translator_to_sdp(sdp_state->translator, remote);
+	if (!sdp) {
+		return -1;
+	}
+
+	sdp_state->remote_sdp = remote;
+	/* TODO Convert the remote SDP into a topology and store that in 
+	 * sdp_state->remote_capabilities
+	 */
+
+	switch (sdp_state->state) {
+	case SDP_STATE_ANSWERER:
+	default:
+		break;
+	case SDP_STATE_INITIAL:
+		sdp_state->state = SDP_STATE_ANSWERER;
+		break;
+	case SDP_STATE_OFFERER:
+		sdp_state->state = SDP_STATE_NEGOTIATED;
+		/* Fall through */
+	case SDP_STATE_NEGOTIATED:
+		/* If state is already negotiated, and we receive a new
+		 * remote SDP, we need to re-create the joint SDP and joint
+		 * capabilities
+		 */
+		merge_sdps(sdp_state);
+		break;
+	}
+
+	return 0;
+}
+
+int ast_sdp_state_reset(struct ast_sdp_state *sdp_state)
+{
+	ast_assert(sdp_state != NULL);
+
+	ast_sdp_free(sdp_state->local_sdp);
+	sdp_state->local_sdp = NULL;
+
+	ast_sdp_free(sdp_state->remote_sdp);
+	sdp_state->remote_sdp = NULL;
+
+	ast_sdp_free(sdp_state->joint_sdp);
+	sdp_state->joint_sdp = NULL;
+
+	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;
+
+	sdp_state->state = SDP_STATE_INITIAL;
+
+	return 0;
+}

-- 
To view, visit https://gerrit.asterisk.org/5072
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I871c0eb480b4c84d83e91ac5628e7a673e8b89ed
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Mark Michelson <mmichelson at digium.com>



More information about the asterisk-code-review mailing list