|
|
|
|
@ -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);
|
|
|
|
|
|