BuildSystem: Remove unused variables.
[asterisk/asterisk.git] / main / stream.c
index 0fabfc7..61eef25 100644 (file)
@@ -57,6 +57,21 @@ struct ast_stream {
        enum ast_stream_state state;
 
        /*!
+        * \brief Opaque stream data
+        */
+       void *data[AST_STREAM_DATA_SLOT_MAX];
+
+       /*!
+        * \brief What to do with data when the stream is freed
+        */
+       ast_stream_data_free_fn data_free_fn[AST_STREAM_DATA_SLOT_MAX];
+
+       /*!
+        * \brief The group that the stream is part of
+        */
+       int group;
+
+       /*!
         * \brief Name for the stream within the context of the channel it is on
         */
        char name[0];
@@ -69,7 +84,7 @@ struct ast_stream_topology {
        AST_VECTOR(, struct ast_stream *) streams;
 };
 
-struct ast_stream *ast_stream_create(const char *name, enum ast_media_type type)
+struct ast_stream *ast_stream_alloc(const char *name, enum ast_media_type type)
 {
        struct ast_stream *stream;
 
@@ -80,40 +95,60 @@ struct ast_stream *ast_stream_create(const char *name, enum ast_media_type type)
 
        stream->type = type;
        stream->state = AST_STREAM_STATE_INACTIVE;
+       stream->group = -1;
        strcpy(stream->name, S_OR(name, "")); /* Safe */
 
        return stream;
 }
 
-struct ast_stream *ast_stream_clone(const struct ast_stream *stream)
+struct ast_stream *ast_stream_clone(const struct ast_stream *stream, const char *name)
 {
        struct ast_stream *new_stream;
        size_t stream_size;
+       int idx;
+       const char *stream_name;
 
        if (!stream) {
                return NULL;
        }
 
-       stream_size = sizeof(*stream) + strlen(stream->name) + 1;
+       stream_name = name ?: stream->name;
+       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);
+       memcpy(new_stream, stream, sizeof(*new_stream));
+       strcpy(new_stream->name, stream_name); /* Safe */
+       new_stream->group = -1;
        if (new_stream->formats) {
                ao2_ref(new_stream->formats, +1);
        }
 
+       /* We cannot clone the opaque data because we don't know how. */
+       for (idx = 0; idx < AST_STREAM_DATA_SLOT_MAX; ++idx) {
+               new_stream->data[idx] = NULL;
+               new_stream->data_free_fn[idx] = NULL;
+       }
+
        return new_stream;
 }
 
-void ast_stream_destroy(struct ast_stream *stream)
+void ast_stream_free(struct ast_stream *stream)
 {
+       int i;
+
        if (!stream) {
                return;
        }
 
+       for (i = 0; i < AST_STREAM_DATA_SLOT_MAX; i++) {
+               if (stream->data_free_fn[i]) {
+                       stream->data_free_fn[i](stream->data[i]);
+               }
+       }
+
        ao2_cleanup(stream->formats);
        ast_free(stream);
 }
@@ -168,6 +203,59 @@ void ast_stream_set_state(struct ast_stream *stream, enum ast_stream_state state
        stream->state = state;
 }
 
+const char *ast_stream_state2str(enum ast_stream_state state)
+{
+       switch (state) {
+       case AST_STREAM_STATE_REMOVED:
+               return "removed";
+       case AST_STREAM_STATE_SENDRECV:
+               return "sendrecv";
+       case AST_STREAM_STATE_SENDONLY:
+               return "sendonly";
+       case AST_STREAM_STATE_RECVONLY:
+               return "recvonly";
+       case AST_STREAM_STATE_INACTIVE:
+               return "inactive";
+       default:
+               return "<unknown>";
+       }
+}
+
+enum ast_stream_state ast_stream_str2state(const char *str)
+{
+       if (!strcmp("sendrecv", str)) {
+               return AST_STREAM_STATE_SENDRECV;
+       }
+       if (!strcmp("sendonly", str)) {
+               return AST_STREAM_STATE_SENDONLY;
+       }
+       if (!strcmp("recvonly", str)) {
+               return AST_STREAM_STATE_RECVONLY;
+       }
+       if (!strcmp("inactive", str)) {
+               return AST_STREAM_STATE_INACTIVE;
+       }
+       return AST_STREAM_STATE_REMOVED;
+}
+
+void *ast_stream_get_data(struct ast_stream *stream, enum ast_stream_data_slot slot)
+{
+       ast_assert(stream != NULL);
+
+       return stream->data[slot];
+}
+
+void *ast_stream_set_data(struct ast_stream *stream, enum ast_stream_data_slot slot,
+       void *data, ast_stream_data_free_fn data_free_fn)
+{
+       ast_assert(stream != NULL);
+
+       stream->data[slot] = data;
+       stream->data_free_fn[slot] = data_free_fn;
+
+       return data;
+}
+
 int ast_stream_get_position(const struct ast_stream *stream)
 {
        ast_assert(stream != NULL);
@@ -176,7 +264,7 @@ int ast_stream_get_position(const struct ast_stream *stream)
 }
 
 #define TOPOLOGY_INITIAL_STREAM_COUNT 2
-struct ast_stream_topology *ast_stream_topology_create(void)
+struct ast_stream_topology *ast_stream_topology_alloc(void)
 {
        struct ast_stream_topology *topology;
 
@@ -201,32 +289,81 @@ struct ast_stream_topology *ast_stream_topology_clone(
 
        ast_assert(topology != NULL);
 
-       new_topology = ast_stream_topology_create();
+       new_topology = ast_stream_topology_alloc();
        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));
+               struct ast_stream *existing = AST_VECTOR_GET(&topology->streams, i);
+               struct ast_stream *stream = ast_stream_clone(existing, NULL);
 
                if (!stream || AST_VECTOR_APPEND(&new_topology->streams, stream)) {
-                       ast_stream_destroy(stream);
-                       ast_stream_topology_destroy(new_topology);
+                       ast_stream_free(stream);
+                       ast_stream_topology_free(new_topology);
                        return NULL;
                }
+
+               ast_stream_set_group(stream, ast_stream_get_group(existing));
        }
 
        return new_topology;
 }
 
-void ast_stream_topology_destroy(struct ast_stream_topology *topology)
+int ast_stream_topology_equal(const struct ast_stream_topology *left,
+       const struct ast_stream_topology *right)
+{
+       int index;
+
+       ast_assert(left != NULL);
+       ast_assert(right != NULL);
+
+       if (ast_stream_topology_get_count(left) != ast_stream_topology_get_count(right)) {
+               return 0;
+       }
+
+       for (index = 0; index < ast_stream_topology_get_count(left); ++index) {
+               const struct ast_stream *left_stream = ast_stream_topology_get_stream(left, index);
+               const struct ast_stream *right_stream = ast_stream_topology_get_stream(right, index);
+
+               if (ast_stream_get_type(left_stream) != ast_stream_get_type(right_stream)) {
+                       return 0;
+               }
+
+               if (ast_stream_get_state(left_stream) != ast_stream_get_state(right_stream)) {
+                       return 0;
+               }
+
+               if (!ast_stream_get_formats(left_stream) && ast_stream_get_formats(right_stream) &&
+                       ast_format_cap_count(ast_stream_get_formats(right_stream))) {
+                       /* A NULL format capabilities and an empty format capabilities are the same, as they have
+                        * no formats inside. If one does though... they are not equal.
+                        */
+                       return 0;
+               } else if (!ast_stream_get_formats(right_stream) && ast_stream_get_formats(left_stream) &&
+                       ast_format_cap_count(ast_stream_get_formats(left_stream))) {
+                       return 0;
+               } else if (ast_stream_get_formats(left_stream) && ast_stream_get_formats(right_stream) &&
+                       !ast_format_cap_identical(ast_stream_get_formats(left_stream), ast_stream_get_formats(right_stream))) {
+                       /* But if both are actually present we need to do an actual identical check. */
+                       return 0;
+               }
+
+               if (strcmp(ast_stream_get_name(left_stream), ast_stream_get_name(right_stream))) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+void ast_stream_topology_free(struct ast_stream_topology *topology)
 {
        if (!topology) {
                return;
        }
 
-       AST_VECTOR_CALLBACK_VOID(&topology->streams, ast_stream_destroy);
+       AST_VECTOR_CALLBACK_VOID(&topology->streams, ast_stream_free);
        AST_VECTOR_FREE(&topology->streams);
        ast_free(topology);
 }
@@ -239,6 +376,8 @@ int ast_stream_topology_append_stream(struct ast_stream_topology *topology, stru
                return -1;
        }
 
+       stream->position = AST_VECTOR_SIZE(&topology->streams) - 1;
+
        return AST_VECTOR_SIZE(&topology->streams) - 1;
 }
 
@@ -268,29 +407,52 @@ int ast_stream_topology_set_stream(struct ast_stream_topology *topology,
                return -1;
        }
 
-       existing_stream = AST_VECTOR_GET(&topology->streams, position);
-       ast_stream_destroy(existing_stream);
+       if (position < AST_VECTOR_SIZE(&topology->streams)) {
+               existing_stream = AST_VECTOR_GET(&topology->streams, position);
+               ast_stream_free(existing_stream);
+       }
+
+       stream->position = position;
 
        if (position == AST_VECTOR_SIZE(&topology->streams)) {
-               AST_VECTOR_APPEND(&topology->streams, stream);
-               return 0;
+               return AST_VECTOR_APPEND(&topology->streams, stream);
        }
 
-       stream->position = position;
        return AST_VECTOR_REPLACE(&topology->streams, position, stream);
 }
 
+int ast_stream_topology_del_stream(struct ast_stream_topology *topology,
+       unsigned int position)
+{
+       struct ast_stream *stream;
+
+       ast_assert(topology != NULL);
+
+       if (AST_VECTOR_SIZE(&topology->streams) <= position) {
+               return -1;
+       }
+
+       stream = AST_VECTOR_REMOVE_ORDERED(&topology->streams, position);
+       ast_stream_free(stream);
+
+       /* Fix up higher stream position indices */
+       for (; position < AST_VECTOR_SIZE(&topology->streams); ++position) {
+               stream = AST_VECTOR_GET(&topology->streams, position);
+               stream->position = position;
+       }
+
+       return 0;
+}
+
 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;
+       topology = ast_stream_topology_alloc();
+       if (!topology || !cap || !ast_format_cap_count(cap)) {
+               return topology;
        }
 
        for (type = AST_MEDIA_TYPE_UNKNOWN + 1; type < AST_MEDIA_TYPE_END; type++) {
@@ -303,32 +465,140 @@ struct ast_stream_topology *ast_stream_topology_create_from_format_cap(
 
                new_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
                if (!new_cap) {
-                       ast_stream_topology_destroy(topology);
+                       ast_stream_topology_free(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);
+                       ast_stream_topology_free(topology);
                        return NULL;
                }
 
-               stream = ast_stream_create(ast_codec_media_type2str(type), type);
+               stream = ast_stream_alloc(ast_codec_media_type2str(type), type);
                if (!stream) {
                        ao2_cleanup(new_cap);
-                       ast_stream_topology_destroy(topology);
+                       ast_stream_topology_free(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);
+               if (ast_stream_topology_append_stream(topology, stream) == -1) {
+                       ast_stream_free(stream);
+                       ast_stream_topology_free(topology);
                        return NULL;
                }
        }
 
        return topology;
 }
+
+struct ast_format_cap *ast_format_cap_from_stream_topology(
+    struct ast_stream_topology *topology)
+{
+       struct ast_format_cap *caps;
+       int i;
+
+       ast_assert(topology != NULL);
+
+       caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+       if (!caps) {
+               return NULL;
+       }
+
+       for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {
+               struct ast_stream *stream;
+
+               stream = AST_VECTOR_GET(&topology->streams, i);
+               if (!stream->formats
+                       || stream->state == AST_STREAM_STATE_REMOVED) {
+                       continue;
+               }
+
+               ast_format_cap_append_from_cap(caps, stream->formats, AST_MEDIA_TYPE_UNKNOWN);
+       }
+
+       return caps;
+}
+
+struct ast_stream *ast_stream_topology_get_first_stream_by_type(
+       const struct ast_stream_topology *topology,
+       enum ast_media_type type)
+{
+       int i;
+
+       ast_assert(topology != NULL);
+
+       for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {
+               struct ast_stream *stream;
+
+               stream = AST_VECTOR_GET(&topology->streams, i);
+               if (stream->type == type
+                       && stream->state != AST_STREAM_STATE_REMOVED) {
+                       return stream;
+               }
+       }
+
+       return NULL;
+}
+
+void ast_stream_topology_map(const struct ast_stream_topology *topology,
+       struct ast_vector_int *types, struct ast_vector_int *v0, struct ast_vector_int *v1)
+{
+       int i;
+       int nths[AST_MEDIA_TYPE_END] = {0};
+       int size = ast_stream_topology_get_count(topology);
+
+       /*
+        * Clear out any old mappings and initialize the new ones
+        */
+       AST_VECTOR_FREE(v0);
+       AST_VECTOR_FREE(v1);
+
+       /*
+        * Both vectors are sized to the topology. The media types vector is always
+        * guaranteed to be the size of the given topology or greater.
+        */
+       AST_VECTOR_INIT(v0, size);
+       AST_VECTOR_INIT(v1, size);
+
+       for (i = 0; i < size; ++i) {
+               struct ast_stream *stream = ast_stream_topology_get_stream(topology, i);
+               enum ast_media_type type = ast_stream_get_type(stream);
+               int index = AST_VECTOR_GET_INDEX_NTH(types, ++nths[type],
+                       type, AST_VECTOR_ELEM_DEFAULT_CMP);
+
+               if (index == -1) {
+                       /*
+                        * If a given type is not found for an index level then update the
+                        * media types vector with that type. This keeps the media types
+                        * vector always at the max topology size.
+                        */
+                       AST_VECTOR_APPEND(types, type);
+                       index = AST_VECTOR_SIZE(types) - 1;
+               }
+
+               /*
+                * The mapping is reflexive in the sense that if it maps in one direction
+                * then the reverse direction maps back to the other's index.
+                */
+               AST_VECTOR_REPLACE(v0, i, index);
+               AST_VECTOR_REPLACE(v1, index, i);
+       }
+}
+
+int ast_stream_get_group(const struct ast_stream *stream)
+{
+       ast_assert(stream != NULL);
+
+       return stream->group;
+}
+
+void ast_stream_set_group(struct ast_stream *stream, int group)
+{
+       ast_assert(stream != NULL);
+
+       stream->group = group;
+}