Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion pjmedia/include/pjmedia/conference.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2008-2026 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -233,6 +233,12 @@ typedef struct pjmedia_conf_op_info
*/
pjmedia_conf_op_param op_param;

/**
* The application data specified in pjmedia_conf_param::user_data
* when the conference bridge was created.
*/
void *user_data;

} pjmedia_conf_op_info;

/**
Expand Down Expand Up @@ -373,6 +379,16 @@ typedef struct pjmedia_conf_param
* level using the pjsua2::MediaConfig::confThreadPrio parameter.
*/
int worker_thread_prio;

/**
* Application data, which will be passed back in
* pjmedia_conf_op_info::user_data of the callback set with
* #pjmedia_conf_set_op_cb().
*
* The default value is NULL.
*/
void *user_data;

} pjmedia_conf_param;


Expand Down Expand Up @@ -512,6 +528,9 @@ PJ_DECL(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf );
*
* The callback will most likely be called from media threads,
* thus application must not perform long/blocking processing in this callback.
*
* The application data specified in pjmedia_conf_param::user_data is
* passed to the callback in pjmedia_conf_op_info::user_data.
*
* @param conf The conference bridge.
* @param cb Callback to be called. Set this to NULL to unregister
Expand Down
9 changes: 8 additions & 1 deletion pjmedia/src/pjmedia/conf_thread.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2008-2026 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -379,6 +379,7 @@ struct pjmedia_conf
op_entry *op_queue; /**< Queue of operations. */
op_entry *op_queue_free;/**< Queue of free entries. */
pjmedia_conf_op_cb cb; /**< OP callback. */
void *user_data; /**< OP callback user data. */


pj_atomic_slist *unused_slots; /**< Unused port's slots. */
Expand Down Expand Up @@ -579,6 +580,8 @@ static void handle_op_queue(pjmedia_conf *conf)
pjmedia_conf_op_info info = { 0 };

pj_log_push_indent();
info.conf = conf;
info.user_data = conf->user_data;
info.op_type = type;
info.status = status;
info.op_param = param;
Expand Down Expand Up @@ -1096,6 +1099,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_create2(pj_pool_t *pool_,

conf->options = param->options;
conf->max_ports = param->max_slots;
conf->user_data = param->user_data;
conf->sampling_rate = param->sampling_rate;
conf->channel_count = param->channel_count;
conf->samples_per_frame = param->samples_per_frame;
Expand Down Expand Up @@ -1339,6 +1343,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf )

pj_log_push_indent();
op_info.conf = conf;
op_info.user_data = conf->user_data;
op_info.op_type = PJMEDIA_CONF_OP_REMOVE_PORT;
op_info.status = status;
op_info.op_param = oprm;
Expand Down Expand Up @@ -2418,6 +2423,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
if (conf->cb) {
pj_log_push_indent();
op_info.conf = conf;
op_info.user_data = conf->user_data;
op_info.status = PJ_ECANCELLED;
(*conf->cb)(&op_info);
pj_log_pop_indent();
Expand Down Expand Up @@ -2503,6 +2509,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,

pj_log_push_indent();
op_info.conf = conf;
op_info.user_data = conf->user_data;
op_info.op_type = PJMEDIA_CONF_OP_REMOVE_PORT;
op_info.status = status;
op_info.op_param = prm;
Expand Down
24 changes: 18 additions & 6 deletions pjmedia/src/pjmedia/conference.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2008-2026 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -267,6 +267,7 @@ struct pjmedia_conf
op_entry *op_queue; /**< Queue of operations. */
op_entry *op_queue_free;/**< Queue of free entries. */
pjmedia_conf_op_cb cb; /**< OP callback. */
void *user_data; /**< OP callback user data. */
};


Expand Down Expand Up @@ -396,6 +397,7 @@ static void handle_op_queue(pjmedia_conf *conf)

pj_log_push_indent();
info.conf = conf;
info.user_data = conf->user_data;
info.op_type = type;
info.status = status;
info.op_param = param;
Expand Down Expand Up @@ -793,11 +795,18 @@ PJ_DEF(pj_status_t) pjmedia_conf_create2(pj_pool_t *pool,
const pjmedia_conf_param *param,
pjmedia_conf **p_conf)
{
PJ_ASSERT_RETURN(param, PJ_EINVAL);
return pjmedia_conf_create(pool,
param->max_slots, param->sampling_rate,
param->channel_count, param->samples_per_frame,
param->bits_per_sample, param->options, p_conf);
pj_status_t status;

PJ_ASSERT_RETURN(param && p_conf, PJ_EINVAL);
status = pjmedia_conf_create(pool, param->max_slots,
param->sampling_rate, param->channel_count,
param->samples_per_frame,
param->bits_per_sample, param->options,
p_conf);
if (status == PJ_SUCCESS)
(*p_conf)->user_data = param->user_data;

return status;
}

/*
Expand Down Expand Up @@ -965,6 +974,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf )

pj_log_push_indent();
op_info.conf = conf;
op_info.user_data = conf->user_data;
op_info.op_type = PJMEDIA_CONF_OP_REMOVE_PORT;
op_info.status = status;
op_info.op_param = oprm;
Expand Down Expand Up @@ -1919,6 +1929,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,
if (conf->cb) {
pj_log_push_indent();
op_info.conf = conf;
op_info.user_data = conf->user_data;
op_info.status = PJ_ECANCELLED;
(*conf->cb)(&op_info);
pj_log_pop_indent();
Expand Down Expand Up @@ -2006,6 +2017,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf,

pj_log_push_indent();
op_info.conf = conf;
op_info.user_data = conf->user_data;
op_info.op_type = PJMEDIA_CONF_OP_REMOVE_PORT;
op_info.status = status;
op_info.op_param = prm;
Expand Down
143 changes: 143 additions & 0 deletions pjmedia/src/test/conf_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,141 @@ static int worker_prio_test(void)
}


#if PJMEDIA_CONF_BACKEND != PJMEDIA_CONF_SWITCH_BOARD_BACKEND
/* The operation callbacks delivered by the bridge, and how many of them
* carried the bridge and the user data it was created with.
*/
typedef struct op_record
{
pjmedia_conf *conf;
unsigned count;
unsigned ok_cnt;
pjmedia_conf_op_type types[8];
pj_status_t statuses[8];
} op_record;

static op_record g_ops;
static int g_op_user_data;

static void record_op(const pjmedia_conf_op_info *info)
{
if (g_ops.count < PJ_ARRAY_SIZE(g_ops.types)) {
g_ops.types[g_ops.count] = info->op_type;
g_ops.statuses[g_ops.count] = info->status;
}
if (info->conf == g_ops.conf && info->user_data == &g_op_user_data)
++g_ops.ok_cnt;
++g_ops.count;
}

static void reset_ops(pjmedia_conf *conf)
{
pj_bzero(&g_ops, sizeof(g_ops));
g_ops.conf = conf;
}

/* Every operation callback carries pjmedia_conf_param::user_data: the
* queued operations, the cancelled operations and the synchronous removal
* of a port whose add-op is still queued, and the removals on destroy.
*/
static int op_user_data_test(void)
{
pj_pool_t *pool = NULL;
pjmedia_conf *conf = NULL;
pjmedia_conf_param param;
pjmedia_port *master, *p1 = NULL, *p2 = NULL;
unsigned slot1 = 0, slot2 = 0;
int rc = 0;
pj_status_t status;

PJ_LOG(3, (THIS_FILE, " conf op callback user data"));

pool = pj_pool_create(mem, "conf_op_ud", 4000, 4000, NULL);
if (!pool) return -500;

pjmedia_conf_param_default(&param);
param.max_slots = 8;
param.sampling_rate = CLOCK_RATE;
param.channel_count = CHANNELS;
param.samples_per_frame = SPF;
param.bits_per_sample = BPS;
param.options = PJMEDIA_CONF_NO_DEVICE;
param.user_data = &g_op_user_data;
status = pjmedia_conf_create2(pool, &param, &conf);
if (status != PJ_SUCCESS) { rc = -501; goto on_return; }
status = pjmedia_conf_set_op_cb(conf, &record_op);
if (status != PJ_SUCCESS) { rc = -502; goto on_return; }
master = pjmedia_conf_get_master_port(conf);

status = pjmedia_null_port_create(pool, CLOCK_RATE, CHANNELS, SPF, BPS,
&p1);
if (status != PJ_SUCCESS) { rc = -503; goto on_return; }
status = pjmedia_null_port_create(pool, CLOCK_RATE, CHANNELS, SPF, BPS,
&p2);
if (status != PJ_SUCCESS) { rc = -504; goto on_return; }

status = pjmedia_conf_add_port(conf, pool, p1, NULL, &slot1);
if (status != PJ_SUCCESS) { rc = -505; goto on_return; }
status = pjmedia_conf_add_port(conf, pool, p2, NULL, &slot2);
if (status != PJ_SUCCESS) { rc = -506; goto on_return; }
status = pjmedia_conf_connect_port(conf, slot1, slot2, 0);
if (status != PJ_SUCCESS) { rc = -507; goto on_return; }

/* No clock tick yet, so removing slot1 cancels its queued add-op and
* connect-op, and removes it synchronously.
*/
reset_ops(conf);
status = pjmedia_conf_remove_port(conf, slot1);
if (status != PJ_SUCCESS) { rc = -510; goto on_return; }
if (g_ops.count != 3 ||
g_ops.types[0] != PJMEDIA_CONF_OP_ADD_PORT ||
g_ops.statuses[0] != PJ_ECANCELLED ||
g_ops.types[1] != PJMEDIA_CONF_OP_CONNECT_PORTS ||
g_ops.statuses[1] != PJ_ECANCELLED ||
g_ops.types[2] != PJMEDIA_CONF_OP_REMOVE_PORT ||
g_ops.statuses[2] != PJ_SUCCESS)
{
PJ_LOG(1, (THIS_FILE, " sync removal: unexpected %u callbacks",
g_ops.count));
rc = -511; goto on_return;
}
if (g_ops.ok_cnt != g_ops.count) { rc = -512; goto on_return; }

/* The add-op of slot2 is completed by the clock */
reset_ops(conf);
status = pump(master, 1);
if (status != PJ_SUCCESS) { rc = -520; goto on_return; }
if (g_ops.count != 1 ||
g_ops.types[0] != PJMEDIA_CONF_OP_ADD_PORT ||
g_ops.statuses[0] != PJ_SUCCESS)
{
PJ_LOG(1, (THIS_FILE, " queued op: unexpected %u callbacks",
g_ops.count));
rc = -521; goto on_return;
}
if (g_ops.ok_cnt != g_ops.count) { rc = -522; goto on_return; }

/* Destroying the bridge removes the remaining ports */
reset_ops(conf);
pjmedia_conf_destroy(conf);
conf = NULL;
if (g_ops.count == 0) { rc = -525; goto on_return; }
if (g_ops.ok_cnt != g_ops.count) { rc = -526; goto on_return; }

on_return:
if (rc != 0 && g_ops.ok_cnt != g_ops.count) {
PJ_LOG(1, (THIS_FILE, " only %u of %u callbacks carried the bridge "
"and its user data", g_ops.ok_cnt, g_ops.count));
}
if (conf)
pjmedia_conf_destroy(conf);
if (pool)
pj_pool_release(pool);
return rc;
}
#endif


int conf_test(void)
{
int rc;
Expand All @@ -663,6 +798,14 @@ int conf_test(void)
return rc;
}

#if PJMEDIA_CONF_BACKEND != PJMEDIA_CONF_SWITCH_BOARD_BACKEND
rc = op_user_data_test();
if (rc != 0) {
PJ_LOG(1,(THIS_FILE, " conf op user data test failed (rc=%d)", rc));
return rc;
}
#endif

rc = detach_replace_test();
if (rc == 1) {
/* Skipped (backend unsupported) - treat as pass. */
Expand Down
Loading