diff --git a/include/asterisk/sdp_state.h b/include/asterisk/sdp_state.h index b5e44179ee..14d3e7c04b 100644 --- a/include/asterisk/sdp_state.h +++ b/include/asterisk/sdp_state.h @@ -55,4 +55,43 @@ struct ast_rtp_instance *ast_sdp_state_get_rtp_instance(struct ast_sdp_state *sd */ 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. + */ +const 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 04de6e385a..b478e71486 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_state { 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 @@ struct ast_sdp_state *ast_sdp_state_alloc(struct ast_stream_topology *streams, s ast_sdp_state_free(sdp_state); return NULL; } + sdp_state->state = SDP_STATE_INITIAL; return sdp_state; } @@ -90,6 +128,8 @@ void ast_sdp_state_free(struct ast_sdp_state *sdp_state) 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_rtp_instance *ast_sdp_state_get_rtp_instance(struct ast_sdp_state *sd 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; +} + +const 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; +}