mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-19 03:07:59 +00:00
core_unreal / core_local: Add multistream and re-negotiation.
When requesting a Local channel the requested stream topology or a converted stream topology will now be placed onto the resulting channels. Frames written in on streams will now also preserve the stream identifier as they are queued on the opposite channel. Finally when a stream topology change is requested it is immediately accepted and reflected on both channels. Each channel also receives a queued frame to indicate that the topology has changed. ASTERISK-28938 Change-Id: I4e9d94da5230d4bd046dc755651493fce1d87186
This commit is contained in:
committed by
Friendly Automation
parent
bbe0f2230d
commit
de2813cf23
@@ -40,6 +40,7 @@
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/bridge.h"
|
||||
#include "asterisk/core_unreal.h"
|
||||
#include "asterisk/stream.h"
|
||||
|
||||
static unsigned int name_sequence = 0;
|
||||
|
||||
@@ -315,6 +316,11 @@ struct ast_frame *ast_unreal_read(struct ast_channel *ast)
|
||||
}
|
||||
|
||||
int ast_unreal_write(struct ast_channel *ast, struct ast_frame *f)
|
||||
{
|
||||
return ast_unreal_write_stream(ast, -1, f);
|
||||
}
|
||||
|
||||
int ast_unreal_write_stream(struct ast_channel *ast, int stream_num, struct ast_frame *f)
|
||||
{
|
||||
struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast);
|
||||
int res = -1;
|
||||
@@ -337,6 +343,9 @@ int ast_unreal_write(struct ast_channel *ast, struct ast_frame *f)
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the frame to reflect the stream */
|
||||
f->stream_num = stream_num;
|
||||
|
||||
/* Just queue for delivery to the other side */
|
||||
ao2_ref(p, 1);
|
||||
ao2_lock(p);
|
||||
@@ -530,6 +539,86 @@ static int unreal_colp_redirect_indicate(struct ast_unreal_pvt *p, struct ast_ch
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Handle stream topology change request.
|
||||
* \since 16.12.0
|
||||
* \since 17.6.0
|
||||
*
|
||||
* \param p Unreal private structure.
|
||||
* \param ast Channel indicating the condition.
|
||||
* \param topology The requested topology.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on error.
|
||||
*/
|
||||
static int unreal_colp_stream_topology_request_change(struct ast_unreal_pvt *p, struct ast_channel *ast, const struct ast_stream_topology *topology)
|
||||
{
|
||||
struct ast_stream_topology *this_channel_topology;
|
||||
struct ast_stream_topology *the_other_channel_topology;
|
||||
int i;
|
||||
struct ast_stream *stream;
|
||||
struct ast_channel *my_chan;
|
||||
struct ast_channel *my_owner;
|
||||
struct ast_channel *this_channel;
|
||||
struct ast_channel *the_other_channel;
|
||||
int res = 0;
|
||||
|
||||
this_channel_topology = ast_stream_topology_clone(topology);
|
||||
if (!this_channel_topology) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
the_other_channel_topology = ast_stream_topology_clone(topology);
|
||||
if (!the_other_channel_topology) {
|
||||
ast_stream_topology_free(this_channel_topology);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We swap the stream state on the other channel because it is as if the channel is
|
||||
* connected to an external endpoint, so the perspective changes.
|
||||
*/
|
||||
for (i = 0; i < ast_stream_topology_get_count(the_other_channel_topology); ++i) {
|
||||
stream = ast_stream_topology_get_stream(the_other_channel_topology, i);
|
||||
|
||||
if (ast_stream_get_state(stream) == AST_STREAM_STATE_RECVONLY) {
|
||||
ast_stream_set_state(stream, AST_STREAM_STATE_SENDONLY);
|
||||
} else if (ast_stream_get_state(stream) == AST_STREAM_STATE_SENDONLY) {
|
||||
ast_stream_set_state(stream, AST_STREAM_STATE_RECVONLY);
|
||||
}
|
||||
}
|
||||
|
||||
ast_channel_unlock(ast);
|
||||
ast_unreal_lock_all(p, &my_chan, &my_owner);
|
||||
if (AST_UNREAL_IS_OUTBOUND(ast, p)) {
|
||||
this_channel = p->chan;
|
||||
the_other_channel = p->owner;
|
||||
} else {
|
||||
this_channel = p->owner;
|
||||
the_other_channel = p->chan;
|
||||
}
|
||||
if (this_channel) {
|
||||
ast_channel_set_stream_topology(this_channel, this_channel_topology);
|
||||
ast_queue_control(this_channel, AST_CONTROL_STREAM_TOPOLOGY_CHANGED);
|
||||
}
|
||||
if (the_other_channel) {
|
||||
ast_channel_set_stream_topology(the_other_channel, the_other_channel_topology);
|
||||
ast_channel_stream_topology_changed_externally(the_other_channel);
|
||||
}
|
||||
if (my_chan) {
|
||||
ast_channel_unlock(my_chan);
|
||||
ast_channel_unref(my_chan);
|
||||
}
|
||||
if (my_owner) {
|
||||
ast_channel_unlock(my_owner);
|
||||
ast_channel_unref(my_owner);
|
||||
}
|
||||
ao2_unlock(p);
|
||||
ast_channel_lock(ast);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_unreal_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
|
||||
{
|
||||
struct ast_unreal_pvt *p = ast_channel_tech_pvt(ast);
|
||||
@@ -583,6 +672,11 @@ int ast_unreal_indicate(struct ast_channel *ast, int condition, const void *data
|
||||
unreal_queue_indicate(p, ast, condition, data, datalen);
|
||||
res = -1;
|
||||
break;
|
||||
case AST_CONTROL_STREAM_TOPOLOGY_REQUEST_CHANGE:
|
||||
if (ast_channel_is_multistream(ast)) {
|
||||
res = unreal_colp_stream_topology_request_change(p, ast, data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
res = unreal_queue_indicate(p, ast, condition, data, datalen);
|
||||
break;
|
||||
@@ -916,9 +1010,28 @@ void ast_unreal_destructor(void *vdoomed)
|
||||
|
||||
ao2_cleanup(doomed->reqcap);
|
||||
doomed->reqcap = NULL;
|
||||
ast_stream_topology_free(doomed->reqtopology);
|
||||
doomed->reqtopology = NULL;
|
||||
}
|
||||
|
||||
struct ast_unreal_pvt *ast_unreal_alloc(size_t size, ao2_destructor_fn destructor, struct ast_format_cap *cap)
|
||||
{
|
||||
struct ast_stream_topology *topology;
|
||||
struct ast_unreal_pvt *unreal;
|
||||
|
||||
topology = ast_stream_topology_create_from_format_cap(cap);
|
||||
if (!topology) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unreal = ast_unreal_alloc_stream_topology(size, destructor, topology);
|
||||
|
||||
ast_stream_topology_free(topology);
|
||||
|
||||
return unreal;
|
||||
}
|
||||
|
||||
struct ast_unreal_pvt *ast_unreal_alloc_stream_topology(size_t size, ao2_destructor_fn destructor, struct ast_stream_topology *topology)
|
||||
{
|
||||
struct ast_unreal_pvt *unreal;
|
||||
|
||||
@@ -935,12 +1048,17 @@ struct ast_unreal_pvt *ast_unreal_alloc(size_t size, ao2_destructor_fn destructo
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unreal->reqcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
|
||||
unreal->reqtopology = ast_stream_topology_clone(topology);
|
||||
if (!unreal->reqtopology) {
|
||||
ao2_ref(unreal, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unreal->reqcap = ast_format_cap_from_stream_topology(topology);
|
||||
if (!unreal->reqcap) {
|
||||
ao2_ref(unreal, -1);
|
||||
return NULL;
|
||||
}
|
||||
ast_format_cap_append_from_cap(unreal->reqcap, cap, AST_MEDIA_TYPE_UNKNOWN);
|
||||
|
||||
memcpy(&unreal->jb_conf, &jb_conf, sizeof(unreal->jb_conf));
|
||||
|
||||
@@ -958,6 +1076,7 @@ struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p,
|
||||
struct ast_assigned_ids id1 = {NULL, NULL};
|
||||
struct ast_assigned_ids id2 = {NULL, NULL};
|
||||
int generated_seqno = ast_atomic_fetchadd_int((int *) &name_sequence, +1);
|
||||
struct ast_stream_topology *topology;
|
||||
|
||||
/* set unique ids for the two channels */
|
||||
if (assignedids && !ast_strlen_zero(assignedids->uniqueid)) {
|
||||
@@ -975,6 +1094,14 @@ struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p,
|
||||
id2.uniqueid = uniqueid2;
|
||||
}
|
||||
|
||||
/* We need to create a topology to place on the first channel, as we can't
|
||||
* share a single one between both.
|
||||
*/
|
||||
topology = ast_stream_topology_clone(p->reqtopology);
|
||||
if (!topology) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate two new Asterisk channels
|
||||
*
|
||||
@@ -987,6 +1114,7 @@ struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p,
|
||||
"%s/%s-%08x;1", tech->type, p->name, (unsigned)generated_seqno);
|
||||
if (!owner) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate owner channel structure\n");
|
||||
ast_stream_topology_free(topology);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1000,6 +1128,10 @@ struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p,
|
||||
|
||||
ast_channel_nativeformats_set(owner, p->reqcap);
|
||||
|
||||
if (ast_channel_is_multistream(owner)) {
|
||||
ast_channel_set_stream_topology(owner, topology);
|
||||
}
|
||||
|
||||
/* Determine our read/write format and set it on each channel */
|
||||
fmt = ast_format_cap_get_format(p->reqcap, 0);
|
||||
if (!fmt) {
|
||||
@@ -1054,6 +1186,11 @@ struct ast_channel *ast_unreal_new_channels(struct ast_unreal_pvt *p,
|
||||
|
||||
ast_channel_nativeformats_set(chan, p->reqcap);
|
||||
|
||||
if (ast_channel_is_multistream(chan)) {
|
||||
ast_channel_set_stream_topology(chan, p->reqtopology);
|
||||
p->reqtopology = NULL;
|
||||
}
|
||||
|
||||
/* Format was already determined when setting up owner */
|
||||
ast_channel_set_writeformat(chan, fmt);
|
||||
ast_channel_set_rawwriteformat(chan, fmt);
|
||||
|
Reference in New Issue
Block a user