diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h new file mode 100644 index 0000000000000000000000000000000000000000..e73ed3fe7a7881bc350f441f8621ab7e66be3943 --- /dev/null +++ b/include/asterisk/stream.h @@ -0,0 +1,182 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2017, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Media Stream API + * + * \author Joshua Colp <jcolp@digium.com> + */ + +#ifndef _AST_STREAM_H_ +#define _AST_STREAM_H_ + +#include "asterisk/codec.h" + +/*! + * \brief Forward declaration for a stream, as it is opaque + */ +struct ast_stream; + +/*! + * \brief Forward declaration for a format capability + */ +struct ast_format_cap; + +/*! + * \brief States that a stream may be in + */ +enum ast_stream_state { + /*! + * \brief Set when the stream has been removed + */ + AST_STREAM_STATE_REMOVED = 0, + /*! + * \brief Set when the stream is sending and receiving media + */ + AST_STREAM_STATE_SENDRECV, + /*! + * \brief Set when the stream is sending media only + */ + AST_STREAM_STATE_SENDONLY, + /*! + * \brief Set when the stream is receiving media only + */ + AST_STREAM_STATE_RECVONLY, + /*! + * \brief Set when the stream is not sending OR receiving media + */ + AST_STREAM_STATE_INACTIVE, +}; + +/*! + * \brief Create a new media stream representation + * + * \param name A name for the stream + * \param type The media type the stream is handling + * + * \retval non-NULL success + * \retval NULL failure + * + * \note This is NOT an AO2 object and has no locking. It is expected that a higher level object provides protection. + * + * \note The stream will default to an inactive state until changed. + * + * \since 15 + */ +struct ast_stream *ast_stream_create(const char *name, enum ast_media_type type); + +/*! + * \brief Destroy a media stream representation + * + * \param stream The media stream + * + * \since 15 + */ +void ast_stream_destroy(struct ast_stream *stream); + +/*! + * \brief Get the name of a stream + * + * \param stream The media stream + * + * \return The name of the stream + * + * \since 15 + */ +const char *ast_stream_get_name(const struct ast_stream *stream); + +/*! + * \brief Get the media type of a stream + * + * \param stream The media stream + * + * \return The media type of the stream + * + * \since 15 + */ +enum ast_media_type ast_stream_get_type(const struct ast_stream *stream); + +/*! + * \brief Change the media type of a stream + * + * \param stream The media stream + * \param type The new media type + * + * \since 15 + */ +void ast_stream_set_type(struct ast_stream *stream, enum ast_media_type type); + +/*! + * \brief Get the current negotiated formats of a stream + * + * \param stream The media stream + * + * \return The negotiated media formats + * + * \note The reference count is not increased + * + * \since 15 + */ +struct ast_format_cap *ast_stream_get_formats(const struct ast_stream *stream); + +/*! + * \brief Set the current negotiated formats of a stream + * + * \param stream The media stream + * \param caps The current negotiated formats + * + * \since 15 + */ +void ast_stream_set_formats(struct ast_stream *stream, struct ast_format_cap *caps); + +/*! + * \brief Get the current state of a stream + * + * \param stream The media stream + * + * \return The state of the stream + * + * \since 15 + */ +enum ast_stream_state ast_stream_get_state(const struct ast_stream *stream); + +/*! + * \brief Set the state of a stream + * + * \param stream The media stream + * \param state The new state that the stream is in + * + * \note Used by stream creator to update internal state + * + * \since 15 + */ +void ast_stream_set_state(struct ast_stream *stream, enum ast_stream_state state); + +/*! + * \brief Get the number of the stream + * + * \param stream The media stream + * + * \return The number of the stream + * + * \since 15 + */ +unsigned int ast_stream_get_num(const struct ast_stream *stream); + +#endif /* _AST_STREAM_H */ diff --git a/main/stream.c b/main/stream.c new file mode 100644 index 0000000000000000000000000000000000000000..fb3dbd5ce5d274ba986ea8e1af0db4f71b53bd66 --- /dev/null +++ b/main/stream.c @@ -0,0 +1,128 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2017, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \brief Media Stream API + * + * \author Joshua Colp <jcolp@digium.com> + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include "asterisk/logger.h" +#include "asterisk/stream.h" +#include "asterisk/strings.h" + +struct ast_stream { + /*! + * \brief The type of media the stream is handling + */ + enum ast_media_type type; + + /*! + * \brief Unique number for the stream within the context of the channel it is on + */ + unsigned int num; + + /*! + * \brief Current formats negotiated on the stream + */ + struct ast_format_cap *formats; + + /*! + * \brief The current state of the stream + */ + enum ast_stream_state state; + + /*! + * \brief Name for the stream within the context of the channel it is on + */ + char name[0]; +}; + +struct ast_stream *ast_stream_create(const char *name, enum ast_media_type type) +{ + struct ast_stream *stream; + + stream = ast_calloc(1, sizeof(*stream) + strlen(S_OR(name, "")) + 1); + if (!stream) { + return NULL; + } + + stream->type = type; + stream->state = AST_STREAM_STATE_INACTIVE; + strcpy(stream->name, S_OR(name, "")); + + return stream; +} + +void ast_stream_destroy(struct ast_stream *stream) +{ + if (!stream) { + return; + } + + ao2_cleanup(stream->formats); + ast_free(stream); +} + +const char *ast_stream_get_name(const struct ast_stream *stream) +{ + return stream->name; +} + +enum ast_media_type ast_stream_get_type(const struct ast_stream *stream) +{ + return stream->type; +} + +void ast_stream_set_type(struct ast_stream *stream, enum ast_media_type type) +{ + stream->type = type; +} + +struct ast_format_cap *ast_stream_get_formats(const struct ast_stream *stream) +{ + return stream->formats; +} + +void ast_stream_set_formats(struct ast_stream *stream, struct ast_format_cap *caps) +{ + 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; +} + +void ast_stream_set_state(struct ast_stream *stream, enum ast_stream_state state) +{ + stream->state = state; +} + +unsigned int ast_stream_get_num(const struct ast_stream *stream) +{ + return stream->num; +} diff --git a/tests/test_stream.c b/tests/test_stream.c new file mode 100644 index 0000000000000000000000000000000000000000..fd78dda971a576ee3c9944b19d64d31e1236c2de --- /dev/null +++ b/tests/test_stream.c @@ -0,0 +1,245 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2017, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Media Stream API Unit Tests + * + * \author Joshua Colp <jcolp@digium.com> + * + */ + +/*** MODULEINFO + <depend>TEST_FRAMEWORK</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include "asterisk/test.h" +#include "asterisk/module.h" +#include "asterisk/stream.h" +#include "asterisk/format.h" +#include "asterisk/format_cap.h" + +AST_TEST_DEFINE(stream_create) +{ + RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_destroy); + + switch (cmd) { + case TEST_INIT: + info->name = "stream_create"; + info->category = "/main/stream/"; + info->summary = "stream create unit test"; + info->description = + "Test that creating a stream results in a stream with the expected values"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + stream = ast_stream_create("test", AST_MEDIA_TYPE_AUDIO); + if (!stream) { + ast_test_status_update(test, "Failed to create media stream given proper arguments\n"); + return AST_TEST_FAIL; + } + + if (ast_stream_get_state(stream) != AST_STREAM_STATE_INACTIVE) { + ast_test_status_update(test, "Newly created stream does not have expected inactive stream state\n"); + return AST_TEST_FAIL; + } + + if (ast_stream_get_type(stream) != AST_MEDIA_TYPE_AUDIO) { + ast_test_status_update(test, "Newly created stream does not have expected audio media type\n"); + return AST_TEST_FAIL; + } + + if (strcmp(ast_stream_get_name(stream), "test")) { + ast_test_status_update(test, "Newly created stream does not have expected name of test\n"); + return AST_TEST_FAIL; + } + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(stream_create_no_name) +{ + RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_destroy); + + switch (cmd) { + case TEST_INIT: + info->name = "stream_create_no_name"; + info->category = "/main/stream/"; + info->summary = "stream create (without a name) unit test"; + info->description = + "Test that creating a stream with no name works"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + stream = ast_stream_create(NULL, AST_MEDIA_TYPE_AUDIO); + if (!stream) { + ast_test_status_update(test, "Failed to create media stream given proper arguments\n"); + return AST_TEST_FAIL; + } + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(stream_set_type) +{ + RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_destroy); + + switch (cmd) { + case TEST_INIT: + info->name = "stream_set_type"; + info->category = "/main/stream/"; + info->summary = "stream type setting unit test"; + info->description = + "Test that changing the type of a stream works"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + stream = ast_stream_create("test", AST_MEDIA_TYPE_AUDIO); + if (!stream) { + ast_test_status_update(test, "Failed to create media stream given proper arguments\n"); + return AST_TEST_FAIL; + } + + if (ast_stream_get_type(stream) != AST_MEDIA_TYPE_AUDIO) { + ast_test_status_update(test, "Newly created stream does not have expected audio media type\n"); + return AST_TEST_FAIL; + } + + ast_stream_set_type(stream, AST_MEDIA_TYPE_VIDEO); + + if (ast_stream_get_type(stream) != AST_MEDIA_TYPE_VIDEO) { + ast_test_status_update(test, "Changed stream does not have expected video media type\n"); + return AST_TEST_FAIL; + } + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(stream_set_formats) +{ + RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_destroy); + RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup); + + switch (cmd) { + case TEST_INIT: + info->name = "stream_set_formats"; + info->category = "/main/stream/"; + info->summary = "stream formats setting unit test"; + info->description = + "Test that changing the formats of a stream works"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!caps) { + ast_test_status_update(test, "Failed to create a format capabilities structure for testing\n"); + return AST_TEST_FAIL; + } + + stream = ast_stream_create("test", AST_MEDIA_TYPE_AUDIO); + if (!stream) { + ast_test_status_update(test, "Failed to create media stream given proper arguments\n"); + return AST_TEST_FAIL; + } + + ast_stream_set_formats(stream, caps); + + if (ast_stream_get_formats(stream) != caps) { + ast_test_status_update(test, "Changed stream does not have expected formats\n"); + return AST_TEST_FAIL; + } + + ast_stream_set_formats(stream, NULL); + + if (ast_stream_get_formats(stream)) { + ast_test_status_update(test, "Retrieved formats from stream despite removing them\n"); + return AST_TEST_FAIL; + } + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(stream_set_state) +{ + RAII_VAR(struct ast_stream *, stream, NULL, ast_stream_destroy); + + switch (cmd) { + case TEST_INIT: + info->name = "stream_set_state"; + info->category = "/main/stream/"; + info->summary = "stream state setting unit test"; + info->description = + "Test that changing the state of a stream works"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + stream = ast_stream_create("test", AST_MEDIA_TYPE_AUDIO); + if (!stream) { + ast_test_status_update(test, "Failed to create media stream given proper arguments\n"); + return AST_TEST_FAIL; + } + + if (ast_stream_get_state(stream) != AST_STREAM_STATE_INACTIVE) { + ast_test_status_update(test, "Newly created stream does not have expected inactive stream state\n"); + return AST_TEST_FAIL; + } + + ast_stream_set_state(stream, AST_STREAM_STATE_SENDRECV); + + if (ast_stream_get_state(stream) != AST_STREAM_STATE_SENDRECV) { + ast_test_status_update(test, "Changed stream does not have expected sendrecv state\n"); + return AST_TEST_FAIL; + } + + return AST_TEST_PASS; +} + +static int unload_module(void) +{ + AST_TEST_UNREGISTER(stream_create); + AST_TEST_UNREGISTER(stream_create_no_name); + AST_TEST_UNREGISTER(stream_set_type); + AST_TEST_UNREGISTER(stream_set_formats); + AST_TEST_UNREGISTER(stream_set_state); + return 0; +} + +static int load_module(void) +{ + AST_TEST_REGISTER(stream_create); + AST_TEST_REGISTER(stream_create_no_name); + AST_TEST_REGISTER(stream_set_type); + AST_TEST_REGISTER(stream_set_formats); + AST_TEST_REGISTER(stream_set_state); + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Media Stream API test module");