summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--channels/chan_iax2.c4
-rw-r--r--funcs/func_frame_trace.c6
-rw-r--r--include/asterisk/channel.h37
-rw-r--r--include/asterisk/frame.h2
-rw-r--r--main/channel.c41
-rw-r--r--tests/test_stream.c239
6 files changed, 329 insertions, 0 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index e2f575d04..6d2eda3f0 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -1430,6 +1430,10 @@ static int iax2_is_control_frame_allowed(int subtype)
/* Intended only for the sending machine's local channel structure. */
case AST_CONTROL_MASQUERADE_NOTIFY:
/* Intended only for masquerades when calling ast_indicate_data(). */
+ case AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE:
+ /* Intended only for internal stream topology manipulation. */
+ case AST_CONTROL_STREAM_TOPOLOGY_CHANGED:
+ /* Intended only for internal stream topology change notification. */
case AST_CONTROL_STREAM_STOP:
case AST_CONTROL_STREAM_SUSPEND:
case AST_CONTROL_STREAM_RESTART:
diff --git a/funcs/func_frame_trace.c b/funcs/func_frame_trace.c
index 8a0b3dd59..49abfdf14 100644
--- a/funcs/func_frame_trace.c
+++ b/funcs/func_frame_trace.c
@@ -336,6 +336,12 @@ static void print_frame(struct ast_frame *frame)
/* Should never happen. */
ast_assert(0);
break;
+ case AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE:
+ ast_verbose("SubClass: STREAM_TOPOLOGY_REQUEST_CHANGE\n");
+ break;
+ case AST_CONTROL_STREAM_TOPOLOGY_CHANGED:
+ ast_verbose("SubClass: STREAM_TOPOLOGY_CHANGED\n");
+ break;
case AST_CONTROL_STREAM_STOP:
ast_verbose("SubClass: STREAM_STOP\n");
break;
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 3ae1e2fd4..9a3a967e2 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -4850,4 +4850,41 @@ struct ast_stream *ast_channel_get_default_stream(struct ast_channel *chan, enum
*/
int ast_channel_is_multistream(struct ast_channel *chan);
+/*!
+ * \brief Request that the stream topology of a channel change
+ *
+ * \param chan The channel to change
+ * \param topology The new stream topology
+ *
+ * \pre chan is locked
+ *
+ * \retval 0 request has been accepted to be attempted
+ * \retval -1 request could not be attempted
+ *
+ * \note This function initiates an asynchronous request to change the stream topology. It is not
+ * guaranteed that the topology will change and until an AST_CONTROL_STREAM_TOPOLOGY_CHANGED
+ * frame is received from the channel the current handler of the channel must tolerate the
+ * stream topology as it currently exists.
+ *
+ * \note This interface is provided for applications and resources to request that the topology change.
+ * It is not for use by the channel driver itself.
+ */
+int ast_channel_request_stream_topology_change(struct ast_channel *chan, struct ast_stream_topology *topology);
+
+/*!
+ * \brief Provide notice to a channel that the stream topology has changed
+ *
+ * \param chan The channel to provide notice to
+ * \param topology The new stream topology
+ *
+ * \pre chan is locked
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ *
+ * \note This interface is provided for applications and resources to accept a topology change.
+ * It is not for use by the channel driver itself.
+ */
+int ast_channel_stream_topology_changed(struct ast_channel *chan, struct ast_stream_topology *topology);
+
#endif /* _ASTERISK_CHANNEL_H */
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index c56539af4..2f6c365ad 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -297,6 +297,8 @@ enum ast_control_frame_type {
AST_CONTROL_UPDATE_RTP_PEER = 32, /*!< Interrupt the bridge and have it update the peer */
AST_CONTROL_PVT_CAUSE_CODE = 33, /*!< Contains an update to the protocol-specific cause-code stored for branching dials */
AST_CONTROL_MASQUERADE_NOTIFY = 34, /*!< A masquerade is about to begin/end. (Never sent as a frame but directly with ast_indicate_data().) */
+ AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE = 35, /*!< Channel indication that a stream topology change has been requested */
+ AST_CONTROL_STREAM_TOPOLOGY_CHANGED = 36, /*!< Channel indication that a stream topology change has occurred */
/*
* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
diff --git a/main/channel.c b/main/channel.c
index 12a30e048..15c7fa406 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -4068,6 +4068,19 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int
}
ast_frfree(f);
f = &ast_null_frame;
+ } else if (f->subclass.integer == AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE && dropnondefault) {
+ /* The caller of this function is incapable of handling streams so we don't accept the change request
+ * and stick to the streams currently on the channel.
+ */
+ ast_channel_stream_topology_changed(chan, ast_channel_get_stream_topology(chan));
+ ast_frfree(f);
+ f = &ast_null_frame;
+ } else if (f->subclass.integer == AST_CONTROL_STREAM_TOPOLOGY_CHANGED && dropnondefault) {
+ /* The caller of this function is incapable of handling streams so we absord the notification that the
+ * stream topology has changed.
+ */
+ ast_frfree(f);
+ f = &ast_null_frame;
}
break;
case AST_FRAME_DTMF_END:
@@ -4494,6 +4507,8 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
case AST_CONTROL_UPDATE_RTP_PEER:
case AST_CONTROL_PVT_CAUSE_CODE:
case AST_CONTROL_MASQUERADE_NOTIFY:
+ case AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE:
+ case AST_CONTROL_STREAM_TOPOLOGY_CHANGED:
case AST_CONTROL_STREAM_STOP:
case AST_CONTROL_STREAM_SUSPEND:
case AST_CONTROL_STREAM_REVERSE:
@@ -4792,6 +4807,8 @@ static int indicate_data_internal(struct ast_channel *chan, int _condition, cons
case AST_CONTROL_MCID:
case AST_CONTROL_MASQUERADE_NOTIFY:
case AST_CONTROL_UPDATE_RTP_PEER:
+ case AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE:
+ case AST_CONTROL_STREAM_TOPOLOGY_CHANGED:
case AST_CONTROL_STREAM_STOP:
case AST_CONTROL_STREAM_SUSPEND:
case AST_CONTROL_STREAM_REVERSE:
@@ -11147,3 +11164,27 @@ enum ast_channel_error ast_channel_errno(void)
{
return ast_channel_internal_errno();
}
+
+int ast_channel_request_stream_topology_change(struct ast_channel *chan, struct ast_stream_topology *topology)
+{
+ ast_assert(chan != NULL);
+ ast_assert(topology != NULL);
+
+ if (!ast_channel_is_multistream(chan) || !ast_channel_tech(chan)->indicate) {
+ return -1;
+ }
+
+ return ast_channel_tech(chan)->indicate(chan, AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE, topology, sizeof(topology));
+}
+
+int ast_channel_stream_topology_changed(struct ast_channel *chan, struct ast_stream_topology *topology)
+{
+ ast_assert(chan != NULL);
+ ast_assert(topology != NULL);
+
+ if (!ast_channel_is_multistream(chan) || !ast_channel_tech(chan)->indicate) {
+ return -1;
+ }
+
+ return ast_channel_tech(chan)->indicate(chan, AST_CONTROL_STREAM_TOPOLOGY_CHANGED, topology, sizeof(topology));
+}
diff --git a/tests/test_stream.c b/tests/test_stream.c
index 7c7897697..3bab67c2c 100644
--- a/tests/test_stream.c
+++ b/tests/test_stream.c
@@ -813,6 +813,8 @@ struct mock_channel_pvt {
int frame_count;
int streams;
int frames_per_read;
+ unsigned int indicated_change_request;
+ unsigned int indicated_changed;
};
static struct ast_frame *mock_channel_read(struct ast_channel *chan)
@@ -860,6 +862,19 @@ static int mock_channel_write(struct ast_channel *chan, struct ast_frame *fr)
return 0;
}
+static int mock_channel_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen)
+{
+ struct mock_channel_pvt *pvt = ast_channel_tech_pvt(chan);
+
+ if (condition == AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE) {
+ pvt->indicated_change_request = 1;
+ } else if (condition == AST_CONTROL_STREAM_TOPOLOGY_CHANGED) {
+ pvt->indicated_changed = 1;
+ }
+
+ return 0;
+}
+
static int mock_channel_write_stream(struct ast_channel *chan, int stream_num, struct ast_frame *fr)
{
struct mock_channel_pvt *pvt = ast_channel_tech_pvt(chan);
@@ -1542,6 +1557,222 @@ AST_TEST_DEFINE(stream_read_multistream)
return res;
}
+AST_TEST_DEFINE(stream_topology_change_request_from_application_non_multistream)
+{
+ struct ast_channel_tech tech = {
+ .read = mock_channel_read,
+ .indicate = mock_channel_indicate,
+ .hangup = mock_channel_hangup,
+ };
+ struct ast_channel *mock_channel;
+ struct mock_channel_pvt *pvt;
+ enum ast_test_result_state res = AST_TEST_PASS;
+ int change_res;
+ RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "stream_topology_change_request_from_application_non_multistream";
+ info->category = "/main/stream/";
+ info->summary = "stream topology changing on non-multistream channel test";
+ info->description =
+ "Test that an application trying to change the stream topology of a non-multistream channel gets a failure";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ mock_channel = make_channel(test, 1, &tech);
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+
+ pvt = ast_channel_tech_pvt(mock_channel);
+ pvt->indicated_change_request = 0;
+ pvt->indicated_changed = 0;
+
+ topology = ast_stream_topology_alloc();
+ ast_test_validate_cleanup(test, topology, res, done);
+
+ change_res = ast_channel_request_stream_topology_change(mock_channel, topology);
+
+ ast_test_validate_cleanup(test, change_res == -1, res, done);
+ ast_test_validate_cleanup(test, !pvt->indicated_change_request, res, done);
+
+ change_res = ast_channel_stream_topology_changed(mock_channel, topology);
+
+ ast_test_validate_cleanup(test, change_res == -1, res, done);
+ ast_test_validate_cleanup(test, !pvt->indicated_changed, res, done);
+
+done:
+ ast_hangup(mock_channel);
+
+ return res;
+}
+
+AST_TEST_DEFINE(stream_topology_change_request_from_channel_non_multistream)
+{
+ struct ast_channel_tech tech = {
+ .read_stream = mock_channel_read,
+ .write_stream = mock_channel_write_stream,
+ .indicate = mock_channel_indicate,
+ .hangup = mock_channel_hangup,
+ };
+ struct ast_channel *mock_channel;
+ struct mock_channel_pvt *pvt;
+ enum ast_test_result_state res = AST_TEST_PASS;
+ RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free);
+ struct ast_frame request_change = {
+ .frametype = AST_FRAME_CONTROL,
+ .subclass.integer = AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE,
+ };
+ struct ast_frame *fr = NULL;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "stream_topology_change_request_from_channel_non_multistream";
+ info->category = "/main/stream/";
+ info->summary = "channel requesting stream topology change to non-multistream application test";
+ info->description =
+ "Test that a channel requesting a stream topology change from a non-multistream application does not work";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ mock_channel = make_channel(test, 1, &tech);
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+
+ pvt = ast_channel_tech_pvt(mock_channel);
+ pvt->indicated_changed = 0;
+
+ topology = ast_stream_topology_alloc();
+ ast_test_validate_cleanup(test, topology, res, done);
+
+ request_change.data.ptr = topology;
+ ast_queue_frame(mock_channel, &request_change);
+
+ fr = ast_read(mock_channel);
+ ast_test_validate_cleanup(test, fr, res, done);
+ ast_test_validate_cleanup(test, fr == &ast_null_frame, res, done);
+ ast_test_validate_cleanup(test, pvt->indicated_changed, res, done);
+
+done:
+ if (fr) {
+ ast_frfree(fr);
+ }
+ ast_hangup(mock_channel);
+
+ return res;
+}
+
+AST_TEST_DEFINE(stream_topology_change_request_from_application)
+{
+ struct ast_channel_tech tech = {
+ .read_stream = mock_channel_read,
+ .write_stream = mock_channel_write_stream,
+ .indicate = mock_channel_indicate,
+ .hangup = mock_channel_hangup,
+ };
+ struct ast_channel *mock_channel;
+ struct mock_channel_pvt *pvt;
+ enum ast_test_result_state res = AST_TEST_PASS;
+ int change_res;
+ RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free);
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "stream_topology_change_request_from_application";
+ info->category = "/main/stream/";
+ info->summary = "stream topology change request from application test";
+ info->description =
+ "Test that an application changing the stream topology of a multistream capable channel receives success";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ mock_channel = make_channel(test, 1, &tech);
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+
+ pvt = ast_channel_tech_pvt(mock_channel);
+ pvt->indicated_change_request = 0;
+ pvt->indicated_changed = 0;
+
+ topology = ast_stream_topology_alloc();
+ ast_test_validate_cleanup(test, topology, res, done);
+
+ change_res = ast_channel_request_stream_topology_change(mock_channel, topology);
+
+ ast_test_validate_cleanup(test, !change_res, res, done);
+ ast_test_validate_cleanup(test, pvt->indicated_change_request, res, done);
+
+ change_res = ast_channel_stream_topology_changed(mock_channel, topology);
+
+ ast_test_validate_cleanup(test, !change_res, res, done);
+ ast_test_validate_cleanup(test, pvt->indicated_changed, res, done);
+
+done:
+ ast_hangup(mock_channel);
+
+ return res;
+}
+
+AST_TEST_DEFINE(stream_topology_change_request_from_channel)
+{
+ struct ast_channel_tech tech = {
+ .read_stream = mock_channel_read,
+ .write_stream = mock_channel_write_stream,
+ .indicate = mock_channel_indicate,
+ .hangup = mock_channel_hangup,
+ };
+ struct ast_channel *mock_channel;
+ struct mock_channel_pvt *pvt;
+ enum ast_test_result_state res = AST_TEST_PASS;
+ RAII_VAR(struct ast_stream_topology *, topology, NULL, ast_stream_topology_free);
+ struct ast_frame request_change = {
+ .frametype = AST_FRAME_CONTROL,
+ .subclass.integer = AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE,
+ };
+ struct ast_frame *fr = NULL;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "stream_topology_change_request_from_channel";
+ info->category = "/main/stream/";
+ info->summary = "channel requesting stream topology change to multistream application test";
+ info->description =
+ "Test that a channel requesting a stream topology change from a multistream application works";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ mock_channel = make_channel(test, 1, &tech);
+ ast_test_validate_cleanup(test, mock_channel, res, done);
+
+ pvt = ast_channel_tech_pvt(mock_channel);
+ pvt->indicated_changed = 0;
+
+ topology = ast_stream_topology_alloc();
+ ast_test_validate_cleanup(test, topology, res, done);
+
+ request_change.data.ptr = topology;
+ ast_queue_frame(mock_channel, &request_change);
+
+ fr = ast_read_stream(mock_channel);
+ ast_test_validate_cleanup(test, fr, res, done);
+ ast_test_validate_cleanup(test, fr->frametype == AST_FRAME_CONTROL, res, done);
+ ast_test_validate_cleanup(test, fr->subclass.integer == AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE, res, done);
+ ast_test_validate_cleanup(test, !pvt->indicated_changed, res, done);
+
+done:
+ if (fr) {
+ ast_frfree(fr);
+ }
+ ast_hangup(mock_channel);
+
+ return res;
+}
+
static int unload_module(void)
{
AST_TEST_UNREGISTER(stream_create);
@@ -1562,6 +1793,10 @@ static int unload_module(void)
AST_TEST_UNREGISTER(stream_write_multistream);
AST_TEST_UNREGISTER(stream_read_non_multistream);
AST_TEST_UNREGISTER(stream_read_multistream);
+ AST_TEST_UNREGISTER(stream_topology_change_request_from_application_non_multistream);
+ AST_TEST_UNREGISTER(stream_topology_change_request_from_channel_non_multistream);
+ AST_TEST_UNREGISTER(stream_topology_change_request_from_application);
+ AST_TEST_UNREGISTER(stream_topology_change_request_from_channel);
return 0;
}
@@ -1584,6 +1819,10 @@ static int load_module(void)
AST_TEST_REGISTER(stream_write_multistream);
AST_TEST_REGISTER(stream_read_non_multistream);
AST_TEST_REGISTER(stream_read_multistream);
+ AST_TEST_REGISTER(stream_topology_change_request_from_application_non_multistream);
+ AST_TEST_REGISTER(stream_topology_change_request_from_channel_non_multistream);
+ AST_TEST_REGISTER(stream_topology_change_request_from_application);
+ AST_TEST_REGISTER(stream_topology_change_request_from_channel);
return AST_MODULE_LOAD_SUCCESS;
}