diff --git a/include/asterisk/codec.h b/include/asterisk/codec.h index 3873324b14399b453ae9bd5ab57e9695358acff2..2ae9551816ddd0674a2e321758e60fa331201db5 100644 --- a/include/asterisk/codec.h +++ b/include/asterisk/codec.h @@ -33,6 +33,7 @@ enum ast_media_type { AST_MEDIA_TYPE_VIDEO, AST_MEDIA_TYPE_IMAGE, AST_MEDIA_TYPE_TEXT, + AST_MEDIA_TYPE_END, }; struct ast_module; diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h index e73ed3fe7a7881bc350f441f8621ab7e66be3943..cffe6ea4cb138b56617970f2419c10386df38239 100644 --- a/include/asterisk/stream.h +++ b/include/asterisk/stream.h @@ -38,6 +38,11 @@ struct ast_stream; */ struct ast_format_cap; +/*! + * \brief The topology of a set of streams + */ +struct ast_stream_topology; + /*! * \brief States that a stream may be in */ @@ -90,12 +95,25 @@ struct ast_stream *ast_stream_create(const char *name, enum ast_media_type type) */ void ast_stream_destroy(struct ast_stream *stream); +/*! + * \brief Create a deep clone of an existing stream + * + * \param stream The existing stream + * + * \retval non-NULL success + * \retval NULL failure + * + * \since 15 + */ +struct ast_stream *ast_stream_clone(const struct ast_stream *stream); + /*! * \brief Get the name of a stream * * \param stream The media stream * - * \return The name of the stream + * \retval non-NULL success + * \retval NULL failure * * \since 15 */ @@ -106,7 +124,7 @@ const char *ast_stream_get_name(const struct ast_stream *stream); * * \param stream The media stream * - * \return The media type of the stream + * \return The media type of the stream (AST_MEDIA_TYPE_UNKNOWN on error) * * \since 15 */ @@ -127,7 +145,8 @@ void ast_stream_set_type(struct ast_stream *stream, enum ast_media_type type); * * \param stream The media stream * - * \return The negotiated media formats + * \retval non-NULL success + * \retval NULL failure * * \note The reference count is not increased * @@ -141,6 +160,9 @@ struct ast_format_cap *ast_stream_get_formats(const struct ast_stream *stream); * \param stream The media stream * \param caps The current negotiated formats * + * \note The new format capabilities structure has its refcount bumped and + * any existing format capabilities structure has its refcount decremented. + * * \since 15 */ void ast_stream_set_formats(struct ast_stream *stream, struct ast_format_cap *caps); @@ -150,7 +172,7 @@ void ast_stream_set_formats(struct ast_stream *stream, struct ast_format_cap *ca * * \param stream The media stream * - * \return The state of the stream + * \return The state of the stream (AST_STREAM_STATE_UNKNOWN on error) * * \since 15 */ @@ -169,14 +191,129 @@ enum ast_stream_state ast_stream_get_state(const struct ast_stream *stream); void ast_stream_set_state(struct ast_stream *stream, enum ast_stream_state state); /*! - * \brief Get the number of the stream + * \brief Get the position of the stream in the topology * * \param stream The media stream * - * \return The number of the stream + * \return The position of the stream (-1 on error) + * + * \since 15 + */ +int ast_stream_get_position(const struct ast_stream *stream); + +/*! + * \brief Create a stream topology + * + * \retval non-NULL success + * \retval NULL failure + * + * \since 15 + */ +struct ast_stream_topology *ast_stream_topology_create(void); + +/*! + * \brief Create a deep clone of an existing stream topology + * + * \param topology The existing topology of streams + * + * \retval non-NULL success + * \retval NULL failure + * + * \since 15 + */ +struct ast_stream_topology *ast_stream_topology_clone( + const struct ast_stream_topology *topology); + +/*! + * \brief Destroy a stream topology + * + * \param topology The topology of streams + * + * \note All streams contained within the topology will be destroyed + * + * \since 15 + */ +void ast_stream_topology_destroy(struct ast_stream_topology *topology); + +/*! + * \brief Append a stream to the topology + * + * \param topology The topology of streams + * \param stream The stream to append + * + * \returns the position of the stream in the topology (-1 on error) + * + * \since 15 + */ +int ast_stream_topology_append_stream(struct ast_stream_topology *topology, + struct ast_stream *stream); + +/*! + * \brief Get the number of streams in a topology + * + * \param topology The topology of streams + * + * \return the number of streams (-1 on error) + * + * \since 15 + */ +int ast_stream_topology_get_count(const struct ast_stream_topology *topology); + +/*! + * \brief Get a specific stream from the topology + * + * \param topology The topology of streams + * \param position The topology position to get + * + * \retval non-NULL success + * \retval NULL failure + * + * \since 15 + */ +struct ast_stream *ast_stream_topology_get_stream( + const struct ast_stream_topology *topology, unsigned int position); + +/*! + * \brief Set a specific position in a topology + * + * \param topology The topology of streams + * \param position The topology position to set + * \param stream The stream to put in its place + * + * \retval 0 success + * \retval -1 failure + * + * \note If an existing stream exists it will be destroyed + * + * \note You can overwrite an existing position in the topology or set + * the first unused position. You can't set positions beyond that. + * + * \since 15 + */ +int ast_stream_topology_set_stream(struct ast_stream_topology *topology, + unsigned int position, struct ast_stream *stream); + +/*! + * \brief A helper function that, given a format capabilities structure, + * creates a topology and separates the media types in format_cap into + * separate streams. + * + * \param caps The format capabilities structure + * + * \retval non-NULL success + * \retval NULL failure + * + * \note The format capabilities reference is NOT altered by this function + * since a new format capabilities structure is created for each media type. + * + * \note Each stream will have its name set to the corresponding media type. + * For example: "AST_MEDIA_TYPE_AUDIO". + * + * \note Each stream will be set to the sendrecv state. * * \since 15 */ -unsigned int ast_stream_get_num(const struct ast_stream *stream); +struct ast_stream_topology *ast_stream_topology_create_from_format_cap( + struct ast_format_cap *cap); #endif /* _AST_STREAM_H */ diff --git a/main/format_cap.c b/main/format_cap.c index 1fe342b3107066b91d4dfdaf283b2f246bf39d59..b0897c001c0e9f77640dffd6b362760506a33648 100644 --- a/main/format_cap.c +++ b/main/format_cap.c @@ -268,6 +268,7 @@ int ast_format_cap_append_from_cap(struct ast_format_cap *dst, const struct ast_ { int idx, res = 0; + /* NOTE: The streams API is dependent on the formats being in "preference" order */ for (idx = 0; (idx < AST_VECTOR_SIZE(&src->preference_order)) && !res; ++idx) { struct format_cap_framed *framed = AST_VECTOR_GET(&src->preference_order, idx); diff --git a/main/stream.c b/main/stream.c index fb3dbd5ce5d274ba986ea8e1af0db4f71b53bd66..0fabfc7387d86522368387dff6a066dd313158da 100644 --- a/main/stream.c +++ b/main/stream.c @@ -32,6 +32,8 @@ #include "asterisk/logger.h" #include "asterisk/stream.h" #include "asterisk/strings.h" +#include "asterisk/format.h" +#include "asterisk/format_cap.h" struct ast_stream { /*! @@ -40,9 +42,9 @@ struct ast_stream { enum ast_media_type type; /*! - * \brief Unique number for the stream within the context of the channel it is on + * \brief The position of the stream in the topology */ - unsigned int num; + unsigned int position; /*! * \brief Current formats negotiated on the stream @@ -60,6 +62,13 @@ struct ast_stream { char name[0]; }; +struct ast_stream_topology { + /*! + * \brief A vector of all the streams in this topology + */ + AST_VECTOR(, struct ast_stream *) streams; +}; + struct ast_stream *ast_stream_create(const char *name, enum ast_media_type type) { struct ast_stream *stream; @@ -71,11 +80,34 @@ struct ast_stream *ast_stream_create(const char *name, enum ast_media_type type) stream->type = type; stream->state = AST_STREAM_STATE_INACTIVE; - strcpy(stream->name, S_OR(name, "")); + strcpy(stream->name, S_OR(name, "")); /* Safe */ return stream; } +struct ast_stream *ast_stream_clone(const struct ast_stream *stream) +{ + struct ast_stream *new_stream; + size_t stream_size; + + if (!stream) { + return NULL; + } + + stream_size = sizeof(*stream) + strlen(stream->name) + 1; + new_stream = ast_calloc(1, stream_size); + if (!new_stream) { + return NULL; + } + + memcpy(new_stream, stream, stream_size); + if (new_stream->formats) { + ao2_ref(new_stream->formats, +1); + } + + return new_stream; +} + void ast_stream_destroy(struct ast_stream *stream) { if (!stream) { @@ -88,41 +120,215 @@ void ast_stream_destroy(struct ast_stream *stream) const char *ast_stream_get_name(const struct ast_stream *stream) { + ast_assert(stream != NULL); + return stream->name; } enum ast_media_type ast_stream_get_type(const struct ast_stream *stream) { + ast_assert(stream != NULL); + return stream->type; } void ast_stream_set_type(struct ast_stream *stream, enum ast_media_type type) { + ast_assert(stream != NULL); + stream->type = type; } struct ast_format_cap *ast_stream_get_formats(const struct ast_stream *stream) { + ast_assert(stream != NULL); + return stream->formats; } void ast_stream_set_formats(struct ast_stream *stream, struct ast_format_cap *caps) { + ast_assert(stream != NULL); + ao2_cleanup(stream->formats); stream->formats = ao2_bump(caps); } enum ast_stream_state ast_stream_get_state(const struct ast_stream *stream) { + ast_assert(stream != NULL); + return stream->state; } void ast_stream_set_state(struct ast_stream *stream, enum ast_stream_state state) { + ast_assert(stream != NULL); + stream->state = state; } -unsigned int ast_stream_get_num(const struct ast_stream *stream) +int ast_stream_get_position(const struct ast_stream *stream) +{ + ast_assert(stream != NULL); + + return stream->position; +} + +#define TOPOLOGY_INITIAL_STREAM_COUNT 2 +struct ast_stream_topology *ast_stream_topology_create(void) +{ + struct ast_stream_topology *topology; + + topology = ast_calloc(1, sizeof(*topology)); + if (!topology) { + return NULL; + } + + if (AST_VECTOR_INIT(&topology->streams, TOPOLOGY_INITIAL_STREAM_COUNT)) { + ast_free(topology); + topology = NULL; + } + + return topology; +} + +struct ast_stream_topology *ast_stream_topology_clone( + const struct ast_stream_topology *topology) +{ + struct ast_stream_topology *new_topology; + int i; + + ast_assert(topology != NULL); + + new_topology = ast_stream_topology_create(); + if (!new_topology) { + return NULL; + } + + for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) { + struct ast_stream *stream = + ast_stream_clone(AST_VECTOR_GET(&topology->streams, i)); + + if (!stream || AST_VECTOR_APPEND(&new_topology->streams, stream)) { + ast_stream_destroy(stream); + ast_stream_topology_destroy(new_topology); + return NULL; + } + } + + return new_topology; +} + +void ast_stream_topology_destroy(struct ast_stream_topology *topology) +{ + if (!topology) { + return; + } + + AST_VECTOR_CALLBACK_VOID(&topology->streams, ast_stream_destroy); + AST_VECTOR_FREE(&topology->streams); + ast_free(topology); +} + +int ast_stream_topology_append_stream(struct ast_stream_topology *topology, struct ast_stream *stream) +{ + ast_assert(topology && stream); + + if (AST_VECTOR_APPEND(&topology->streams, stream)) { + return -1; + } + + return AST_VECTOR_SIZE(&topology->streams) - 1; +} + +int ast_stream_topology_get_count(const struct ast_stream_topology *topology) +{ + ast_assert(topology != NULL); + + return AST_VECTOR_SIZE(&topology->streams); +} + +struct ast_stream *ast_stream_topology_get_stream( + const struct ast_stream_topology *topology, unsigned int stream_num) +{ + ast_assert(topology != NULL); + + return AST_VECTOR_GET(&topology->streams, stream_num); +} + +int ast_stream_topology_set_stream(struct ast_stream_topology *topology, + unsigned int position, struct ast_stream *stream) { - return stream->num; + struct ast_stream *existing_stream; + + ast_assert(topology && stream); + + if (position > AST_VECTOR_SIZE(&topology->streams)) { + return -1; + } + + existing_stream = AST_VECTOR_GET(&topology->streams, position); + ast_stream_destroy(existing_stream); + + if (position == AST_VECTOR_SIZE(&topology->streams)) { + AST_VECTOR_APPEND(&topology->streams, stream); + return 0; + } + + stream->position = position; + return AST_VECTOR_REPLACE(&topology->streams, position, stream); +} + +struct ast_stream_topology *ast_stream_topology_create_from_format_cap( + struct ast_format_cap *cap) +{ + struct ast_stream_topology *topology; + enum ast_media_type type; + + ast_assert(cap != NULL); + + topology = ast_stream_topology_create(); + if (!topology) { + return NULL; + } + + for (type = AST_MEDIA_TYPE_UNKNOWN + 1; type < AST_MEDIA_TYPE_END; type++) { + struct ast_format_cap *new_cap; + struct ast_stream *stream; + + if (!ast_format_cap_has_type(cap, type)) { + continue; + } + + new_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!new_cap) { + ast_stream_topology_destroy(topology); + return NULL; + } + + ast_format_cap_set_framing(new_cap, ast_format_cap_get_framing(cap)); + if (ast_format_cap_append_from_cap(new_cap, cap, type)) { + ao2_cleanup(new_cap); + ast_stream_topology_destroy(topology); + return NULL; + } + + stream = ast_stream_create(ast_codec_media_type2str(type), type); + if (!stream) { + ao2_cleanup(new_cap); + ast_stream_topology_destroy(topology); + return NULL; + } + /* We're transferring the initial ref so no bump needed */ + stream->formats = new_cap; + stream->state = AST_STREAM_STATE_SENDRECV; + if (!ast_stream_topology_append_stream(topology, stream)) { + ast_stream_destroy(stream); + ast_stream_topology_destroy(topology); + return NULL; + } + } + + return topology; } diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index 4d6a1a168b98a17838e8561d064747adc260587a..e32d2b65f8d91b9e59f2b0094033d5a12334d21e 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -87,7 +87,8 @@ static int media_type_to_fdno(enum ast_media_type media_type) case AST_MEDIA_TYPE_VIDEO: return FD_VIDEO; case AST_MEDIA_TYPE_TEXT: case AST_MEDIA_TYPE_UNKNOWN: - case AST_MEDIA_TYPE_IMAGE: break; + case AST_MEDIA_TYPE_IMAGE: + case AST_MEDIA_TYPE_END: break; } return -1; }