[Asterisk-code-review] stream: Add media stream topology definition and API (asterisk[master])
George Joseph
asteriskteam at digium.com
Fri Feb 10 15:47:48 CST 2017
George Joseph has uploaded a new change for review. ( https://gerrit.asterisk.org/4922 )
Change subject: stream: Add media stream topology definition and API
......................................................................
stream: Add media stream topology definition and API
This change adds the media stream topology definition and API for
accessing and using it.
Some refactoring of the stream was also done.
ASTERISK-26786
Change-Id: Ic930232d24d5ad66dcabc14e9b359e0ff8e7f568
---
M include/asterisk/codec.h
M include/asterisk/stream.h
M main/format_cap.c
M main/stream.c
M res/res_pjsip_sdp_rtp.c
5 files changed, 410 insertions(+), 18 deletions(-)
git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/22/4922/1
diff --git a/include/asterisk/codec.h b/include/asterisk/codec.h
index 3873324..11270fc 100644
--- a/include/asterisk/codec.h
+++ b/include/asterisk/codec.h
@@ -33,6 +33,7 @@
AST_MEDIA_TYPE_VIDEO,
AST_MEDIA_TYPE_IMAGE,
AST_MEDIA_TYPE_TEXT,
+ AST_MEDIA_TYPE_END,
};
struct ast_module;
@@ -61,6 +62,9 @@
* \brief Retrieve the number of samples in a frame
*
* \param frame The frame to examine
+ }
+
+ *
*
* \return the number of samples
*/
diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h
index e73ed3f..f651da6 100644
--- a/include/asterisk/stream.h
+++ b/include/asterisk/stream.h
@@ -39,13 +39,22 @@
struct ast_format_cap;
/*!
+ * \brief The topology of a set of streams
+ */
+struct ast_stream_topology;
+
+/*!
* \brief States that a stream may be in
*/
enum ast_stream_state {
/*!
+ * \brief Set when the stream state is unknown
+ */
+ AST_STREAM_STATE_UNKNOWN = 0,
+ /*!
* \brief Set when the stream has been removed
*/
- AST_STREAM_STATE_REMOVED = 0,
+ AST_STREAM_STATE_REMOVED,
/*!
* \brief Set when the stream is sending and receiving media
*/
@@ -91,11 +100,24 @@
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 +128,7 @@
*
* \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 +149,8 @@
*
* \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 +164,9 @@
* \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 +176,7 @@
*
* \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 +195,129 @@
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
*/
-unsigned int ast_stream_get_num(const struct ast_stream *stream);
+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
+ */
+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 1fe342b..b0897c0 100644
--- a/main/format_cap.c
+++ b/main/format_cap.c
@@ -268,6 +268,7 @@
{
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 fb3dbd5..89632d4 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 @@
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 @@
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,9 +80,32 @@
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)
@@ -88,41 +120,254 @@
const char *ast_stream_get_name(const struct ast_stream *stream)
{
- return stream->name;
+ return stream ? stream->name : NULL;
}
enum ast_media_type ast_stream_get_type(const struct ast_stream *stream)
{
- return stream->type;
+ return stream ? stream->type : AST_MEDIA_TYPE_UNKNOWN;
}
void ast_stream_set_type(struct ast_stream *stream, enum ast_media_type type)
{
+ if (!stream) {
+ return;
+ }
+
stream->type = type;
}
struct ast_format_cap *ast_stream_get_formats(const struct ast_stream *stream)
{
- return stream->formats;
+ return stream ? stream->formats : NULL;
}
void ast_stream_set_formats(struct ast_stream *stream, struct ast_format_cap *caps)
{
+ if (!stream) {
+ return;
+ }
+
ao2_cleanup(stream->formats);
stream->formats = ao2_bump(caps);
}
enum ast_stream_state ast_stream_get_state(const struct ast_stream *stream)
{
- return stream->state;
+ return stream ? stream->state : AST_STREAM_STATE_UNKNOWN;
}
void ast_stream_set_state(struct ast_stream *stream, enum ast_stream_state state)
{
+ if (!stream) {
+ return;
+ }
+
stream->state = state;
}
-unsigned int ast_stream_get_num(const struct ast_stream *stream)
+int ast_stream_get_position(const struct ast_stream *stream)
{
- return stream->num;
+ return stream ? stream->position : -1;
+}
+
+#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;
+
+ if (!topology) {
+ return 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)
+{
+ if (!topology || !stream) {
+ return -1;
+ }
+
+ 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)
+{
+ return topology ? AST_VECTOR_SIZE(&topology->streams) : -1;
+}
+
+struct ast_stream *ast_stream_topology_get_stream(
+ const struct ast_stream_topology *topology, unsigned int stream_num)
+{
+ return topology ? AST_VECTOR_GET(&topology->streams, stream_num) : NULL;
+}
+
+int ast_stream_topology_set_stream(struct ast_stream_topology *topology,
+ unsigned int position, struct ast_stream *stream)
+{
+ struct ast_stream *existing_stream;
+
+ if (!topology || !stream || position > AST_VECTOR_SIZE(&topology->streams)) {
+ return -1;
+ }
+
+ existing_stream = AST_VECTOR_GET(&topology->streams, position);
+ ast_stream_destroy(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 caps_wrapper {
+ struct ast_format_cap *cap;
+ enum ast_media_type type;
+};
+
+static char *type_names[] = {
+ "AST_MEDIA_TYPE_UNKNOWN",
+ "AST_MEDIA_TYPE_AUDIO",
+ "AST_MEDIA_TYPE_VIDEO",
+ "AST_MEDIA_TYPE_IMAGE",
+ "AST_MEDIA_TYPE_TEXT"
+};
+
+static void wrapper_destructor(struct caps_wrapper *wrapper)
+{
+ ao2_cleanup(wrapper->cap);
+
+ ast_free(wrapper);
+}
+
+struct ast_stream_topology *ast_stream_topology_create_from_format_cap(
+ struct ast_format_cap *cap)
+{
+ struct ast_stream_topology *topology;
+ int i;
+ enum ast_media_type type;
+ AST_VECTOR(, struct caps_wrapper *) new_caps;
+
+ if (!cap) {
+ return NULL;
+ }
+
+ if (AST_VECTOR_INIT(&new_caps, 10)) {
+ return NULL;
+ }
+
+ topology = ast_stream_topology_create();
+ if (!topology) {
+ AST_VECTOR_FREE(&new_caps);
+ return NULL;
+ }
+
+ for (type = AST_MEDIA_TYPE_UNKNOWN + 1; type < AST_MEDIA_TYPE_END; type++) {
+ struct caps_wrapper *wrapper;
+
+ if (!ast_format_cap_has_type(cap, type)) {
+ continue;
+ }
+
+ wrapper = ast_malloc(sizeof(*wrapper));
+ if (!wrapper) {
+ goto error;
+ }
+
+ wrapper->cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+ if (!wrapper->cap) {
+ wrapper_destructor(wrapper);
+ goto error;
+ }
+
+ wrapper->type = type;
+ ast_format_cap_set_framing(wrapper->cap, ast_format_cap_get_framing(cap));
+ ast_format_cap_append_from_cap(wrapper->cap, cap, type);
+
+ if (AST_VECTOR_APPEND(&new_caps, wrapper)) {
+ wrapper_destructor(wrapper);
+ goto error;
+ }
+ }
+
+ for (i = 0; i < AST_VECTOR_SIZE(&new_caps); i++) {
+ struct caps_wrapper *wrapper = AST_VECTOR_GET(&new_caps, i);
+ struct ast_stream *stream = ast_stream_create(type_names[wrapper->type], type);
+
+ if (!stream) {
+ goto error;
+ }
+
+ stream->formats = wrapper->cap;
+ wrapper->cap = NULL;
+ stream->state = AST_STREAM_STATE_SENDRECV;
+ if (!ast_stream_topology_append_stream(topology, stream)) {
+ goto error;
+ }
+ }
+ goto done;
+
+error:
+ ast_stream_topology_destroy(topology);
+ topology = NULL;
+
+done:
+ AST_VECTOR_CALLBACK_VOID(&new_caps, wrapper_destructor);
+ AST_VECTOR_FREE(&new_caps);
+
+ return topology;
}
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 4d6a1a1..e32d2b6 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -87,7 +87,8 @@
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;
}
--
To view, visit https://gerrit.asterisk.org/4922
To unsubscribe, visit https://gerrit.asterisk.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic930232d24d5ad66dcabc14e9b359e0ff8e7f568
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: George Joseph <gjoseph at digium.com>
More information about the asterisk-code-review
mailing list