diff --git a/pjmedia/include/pjmedia/conference.h b/pjmedia/include/pjmedia/conference.h index 9c1b4b4535..9000daaa9f 100644 --- a/pjmedia/include/pjmedia/conference.h +++ b/pjmedia/include/pjmedia/conference.h @@ -348,6 +348,10 @@ PJ_DECL(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, * Disconnect unidirectional audio from the specified source to the specified * sink slot. * + * Note that the operation will be done asynchronously, so application + * should not assume that the port will no longer receive/send audio frame + * after this function has returned. + * * @param conf The conference bridge. * @param src_slot Source slot. * @param sink_slot Sink slot. @@ -362,6 +366,10 @@ PJ_DECL(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, /** * Disconnect unidirectional audio from all sources to the specified sink slot. * + * Note that the operation will be done asynchronously, so application + * should not assume that the port will no longer receive/send audio frame + * after this function has returned. + * * @param conf The conference bridge. * @param sink_slot Sink slot. * @@ -375,6 +383,10 @@ pjmedia_conf_disconnect_port_from_sources( pjmedia_conf *conf, /** * Disconnect unidirectional audio from the specified source to all sink slots. * + * Note that the operation will be done asynchronously, so application + * should not assume that the port will no longer receive/send audio frame + * after this function has returned. + * * @param conf The conference bridge. * @param src_slot Source slot. * @@ -409,6 +421,10 @@ PJ_DECL(unsigned) pjmedia_conf_get_connect_count(pjmedia_conf *conf); /** * Remove the specified port from the conference bridge. * + * Note that the operation will be done asynchronously, so application + * should not assume that the port will no longer receive/send audio frame + * after this function has returned. + * * @param conf The conference bridge. * @param slot The port index to be removed. * diff --git a/pjmedia/include/pjmedia/port.h b/pjmedia/include/pjmedia/port.h index 42eaa138a7..20b1c0e43b 100644 --- a/pjmedia/include/pjmedia/port.h +++ b/pjmedia/include/pjmedia/port.h @@ -416,7 +416,8 @@ typedef struct pjmedia_port pjmedia_frame *frame); /** - * Called to destroy this port. + * Destructor. + * This should only be called by #pjmedia_port_destroy(). */ pj_status_t (*on_destroy)(struct pjmedia_port *this_port); @@ -512,14 +513,21 @@ PJ_DECL(pj_status_t) pjmedia_port_put_frame( pjmedia_port *port, PJ_DECL(pj_status_t) pjmedia_port_destroy( pjmedia_port *port ); + +/* + ******************************************************************* + * Helper functions. + ******************************************************************* + */ + /** * This is a helper function to initialize the port's group lock. This * function will create a group lock if NULL is passed, initialize the group * lock by adding the port's destructor to the group lock handler list, and * increment the reference counter. * - * This function should only be called by a media port implementation and - * after port's on_destroy() function has been assigned. + * This function should only be called by media port implementation. The port + * must have its own pool which will be released in its on_destroy() function. * * @param port The pjmedia port to be initialized. * @param pool The pool, this can be a temporary pool as diff --git a/pjmedia/include/pjmedia/splitcomb.h b/pjmedia/include/pjmedia/splitcomb.h index c9e15be22c..d0b8165edf 100644 --- a/pjmedia/include/pjmedia/splitcomb.h +++ b/pjmedia/include/pjmedia/splitcomb.h @@ -106,7 +106,8 @@ PJ_DECL(pj_status_t) pjmedia_splitcomb_set_channel(pjmedia_port *splitcomb, * media port. So this effectively reverse the phase of the media port. * * @param pool The pool to allocate memory for the port and - * buffers. + * buffers. This is deprecated, the channel will be + * created using splitter/combiner's pool. * @param splitcomb The splitter/combiner. * @param ch_num Audio channel starting number (zero based). * @param options Normally is zero, but the lower 8-bit of the diff --git a/pjmedia/src/pjmedia/avi_player.c b/pjmedia/src/pjmedia/avi_player.c index d22934a545..f0fe2e9922 100644 --- a/pjmedia/src/pjmedia/avi_player.c +++ b/pjmedia/src/pjmedia/avi_player.c @@ -117,6 +117,7 @@ static avi_fmt_info avi_fmts[] = struct pjmedia_avi_streams { + pj_pool_t *pool; unsigned num_streams; pjmedia_port **streams; }; @@ -142,12 +143,12 @@ struct avi_reader_port void (*cb2)(pjmedia_port*, void*); }; - static pj_status_t avi_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t avi_on_destroy(pjmedia_port *this_port); -static struct avi_reader_port *create_avi_port(pj_pool_t *pool) +static struct avi_reader_port *create_avi_port(pj_pool_t *pool, + pj_grp_lock_t *grp_lock) { const pj_str_t name = pj_str("file"); struct avi_reader_port *port; @@ -166,6 +167,8 @@ static struct avi_reader_port *create_avi_port(pj_pool_t *pool) port->base.get_frame = &avi_get_frame; port->base.on_destroy = &avi_on_destroy; + pjmedia_port_init_grp_lock(&port->base, pool, grp_lock); + return port; } @@ -196,11 +199,19 @@ static pj_status_t file_read3(pj_oshandle_t fd, void *data, pj_ssize_t size, return status; } + +static void streams_on_destroy(void *arg) +{ + pjmedia_avi_streams *streams = (pjmedia_avi_streams*)arg; + pj_pool_safe_release(&streams->pool); +} + + /* * Create AVI player port. */ PJ_DEF(pj_status_t) -pjmedia_avi_player_create_streams(pj_pool_t *pool, +pjmedia_avi_player_create_streams(pj_pool_t *pool_, const char *filename, unsigned options, pjmedia_avi_streams **p_streams) @@ -209,20 +220,36 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool, struct avi_reader_port *fport[PJMEDIA_AVI_MAX_NUM_STREAMS]; pj_off_t pos; unsigned i, nstr = 0; + pj_pool_t *pool = NULL; + pj_grp_lock_t *grp_lock = NULL; pj_status_t status = PJ_SUCCESS; /* Check arguments. */ - PJ_ASSERT_RETURN(pool && filename && p_streams, PJ_EINVAL); + PJ_ASSERT_RETURN(pool_ && filename && p_streams, PJ_EINVAL); /* Check the file really exists. */ if (!pj_file_exists(filename)) { return PJ_ENOTFOUND; } + /* Create own pool */ + pool = pj_pool_create(pool_->factory, "aviplayer", 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + + /* Create group lock */ + status = pj_grp_lock_create(pool, NULL, &grp_lock); + if (status != PJ_SUCCESS) + goto on_error; + /* Create fport instance. */ - fport[0] = create_avi_port(pool); + fport[0] = create_avi_port(pool, grp_lock); if (!fport[0]) { - return PJ_ENOMEM; + /* Destroy group lock here to avoid leak */ + pj_grp_lock_destroy(grp_lock); + grp_lock = NULL; + + status = PJ_ENOMEM; + goto on_error; } /* Get the file size. */ @@ -232,14 +259,15 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool, if (fport[0]->fsize <= (pj_off_t)(sizeof(riff_hdr_t) + sizeof(avih_hdr_t) + sizeof(strl_hdr_t))) { - return PJMEDIA_EINVALIMEDIATYPE; + status = PJMEDIA_EINVALIMEDIATYPE; + goto on_error; } /* Open file. */ status = pj_file_open(pool, filename, PJ_O_RDONLY | PJ_O_CLOEXEC, &fport[0]->fd); if (status != PJ_SUCCESS) - return status; + goto on_error; /* Read the RIFF + AVIH header. */ status = file_read(fport[0]->fd, &avi_hdr, @@ -435,7 +463,7 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool, if (nstr > 0) { /* Create fport instance. */ - fport[nstr] = create_avi_port(pool); + fport[nstr] = create_avi_port(pool, grp_lock); if (!fport[nstr]) { status = PJ_ENOMEM; goto on_error; @@ -542,6 +570,13 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool, for (i = 0; i < nstr; i++) (*p_streams)->streams[i] = &fport[i]->base; + status = pj_grp_lock_add_handler(grp_lock, NULL, *p_streams, + &streams_on_destroy); + if (status != PJ_SUCCESS) + goto on_error; + + (*p_streams)->pool = pool; + PJ_LOG(4,(THIS_FILE, "AVI file player '%.*s' created with " "%d media ports", @@ -552,9 +587,13 @@ pjmedia_avi_player_create_streams(pj_pool_t *pool, return PJ_SUCCESS; on_error: - fport[0]->base.on_destroy(&fport[0]->base); - for (i = 1; i < nstr; i++) - fport[i]->base.on_destroy(&fport[i]->base); + if (grp_lock) { + pjmedia_port_destroy(&fport[0]->base); + for (i = 1; i < nstr; i++) + pjmedia_port_destroy(&fport[i]->base); + } + pj_pool_release(pool); + if (status == AVI_EOF) return PJMEDIA_EINVALIMEDIATYPE; return status; diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c index 28d1d5b042..ba3e45dbff 100644 --- a/pjmedia/src/pjmedia/conference.c +++ b/pjmedia/src/pjmedia/conference.c @@ -110,6 +110,7 @@ static FILE *fhnd_rec; */ struct conf_port { + pj_pool_t *pool; /**< Pool. */ pj_str_t name; /**< Port name. */ pjmedia_port *port; /**< get_frame() and put_frame() */ pjmedia_port_op rx_setting; /**< Can we receive from this port */ @@ -221,14 +222,22 @@ struct conf_port * Burst and drift are handled by delay buffer. */ pjmedia_delay_buf *delay_buf; + + pj_bool_t is_new; /**< Newly added port, avoid read/write + data from/to. */ }; +/* Forward declarations */ +typedef struct op_entry op_entry; + + /* * Conference bridge. */ struct pjmedia_conf { + pj_pool_t *pool; /**< Pool */ unsigned options; /**< Bitmask options. */ unsigned max_ports; /**< Maximum ports. */ unsigned port_cnt; /**< Current number of ports. */ @@ -242,6 +251,9 @@ struct pjmedia_conf unsigned channel_count;/**< Number of channels (1=mono). */ unsigned samples_per_frame; /**< Samples per frame. */ unsigned bits_per_sample; /**< Bits per sample. */ + + op_entry *op_queue; /**< Queue of operations. */ + op_entry *op_queue_free;/**< Queue of free entries. */ }; @@ -259,21 +271,153 @@ static pj_status_t destroy_port_pasv(pjmedia_port *this_port); #endif +/* As we don't hold mutex in the clock/get_frame(), some conference operations + * that change conference states need to be synchronized with the clock. + * So some steps of the operations needs to be executed within the clock tick + * context, especially the steps related to changing ports connection. + */ + +/* Synchronized operation type enumeration. */ +typedef enum op_type +{ + OP_UNKNOWN, + OP_REMOVE_PORT, + OP_CONNECT_PORTS, + OP_DISCONNECT_PORTS, + OP_DISCONNECT_PORT_FROM_SOURCES, + OP_DISCONNECT_PORT_FROM_SINKS, +} op_type; + +/* Synchronized operation parameter. */ +typedef union op_param +{ + struct { + unsigned port; + } remove_port; + + struct { + unsigned src; + unsigned sink; + int adj_level; + } connect_ports; + + struct { + unsigned src; + unsigned sink; + } disconnect_ports; + +} op_param; + +/* Synchronized operation list entry. */ +typedef struct op_entry { + PJ_DECL_LIST_MEMBER(struct op_entry); + op_type type; + op_param param; +} op_entry; + +/* Prototypes of synchronized operation */ +static void op_remove_port(pjmedia_conf *conf, const op_param *prm); +static void op_connect_ports(pjmedia_conf *conf, const op_param *prm); +static void op_disconnect_ports(pjmedia_conf *conf, const op_param *prm); + +static op_entry* get_free_op_entry(pjmedia_conf *conf) +{ + op_entry *ope = NULL; + + /* Get from empty list if any, otherwise, allocate a new one */ + if (!pj_list_empty(conf->op_queue_free)) { + ope = conf->op_queue_free->next; + pj_list_erase(ope); + } else { + ope = PJ_POOL_ZALLOC_T(conf->pool, op_entry); + } + return ope; +} + +static void handle_op_queue(pjmedia_conf *conf) +{ + op_entry *op, *next_op; + + op = conf->op_queue->next; + while (op != conf->op_queue) { + next_op = op->next; + pj_list_erase(op); + + switch(op->type) { + case OP_REMOVE_PORT: + op_remove_port(conf, &op->param); + break; + case OP_CONNECT_PORTS: + op_connect_ports(conf, &op->param); + break; + case OP_DISCONNECT_PORTS: + case OP_DISCONNECT_PORT_FROM_SOURCES: + case OP_DISCONNECT_PORT_FROM_SINKS: + op_disconnect_ports(conf, &op->param); + break; + default: + pj_assert(!"Invalid sync-op in conference"); + break; + } + + op->type = OP_UNKNOWN; + pj_list_push_back(conf->op_queue_free, op); + op = next_op; + } +} + + +/* Group lock handler */ +static void conf_port_on_destroy(void *arg) +{ + struct conf_port *conf_port = (struct conf_port*)arg; + if (conf_port->pool) + pj_pool_safe_release(&conf_port->pool); +} + + /* * Create port. */ -static pj_status_t create_conf_port( pj_pool_t *pool, +static pj_status_t create_conf_port( pj_pool_t *parent_pool, pjmedia_conf *conf, pjmedia_port *port, const pj_str_t *name, struct conf_port **p_conf_port) { struct conf_port *conf_port; - pj_status_t status; + pj_pool_t *pool = NULL; + pj_status_t status = PJ_SUCCESS; + + /* Create own pool */ + pool = pj_pool_create(parent_pool->factory, name->ptr, 500, 500, NULL); + if (!pool) { + status = PJ_ENOMEM; + goto on_return; + } /* Create port. */ conf_port = PJ_POOL_ZALLOC_T(pool, struct conf_port); - PJ_ASSERT_RETURN(conf_port, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port, {status = PJ_ENOMEM; goto on_return;}); + conf_port->pool = pool; + + /* Increment port ref count to avoid premature destroy due to + * asynchronous port removal. + */ + if (port) { + if (!port->grp_lock) { + /* Create group lock if it does not have one */ + pjmedia_port_init_grp_lock(port, pool, NULL); + } + + pj_grp_lock_add_ref(port->grp_lock); + + /* Pool may be used for creating port's group lock and the group lock + * may be used by app, so pool destroy must be done via handler. + */ + status = pj_grp_lock_add_handler(port->grp_lock, NULL, conf_port, + &conf_port_on_destroy); + } /* Set name */ pj_strdup_with_null(pool, &conf_port->name, name); @@ -289,12 +433,14 @@ static pj_status_t create_conf_port( pj_pool_t *pool, /* Create transmit flag array */ conf_port->listener_slots = (SLOT_TYPE*) pj_pool_zalloc(pool, conf->max_ports * sizeof(SLOT_TYPE)); - PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port->listener_slots, + {status = PJ_ENOMEM; goto on_return;}); /* Create adjustment level array */ conf_port->listener_adj_level = (unsigned *) pj_pool_zalloc(pool, conf->max_ports * sizeof(unsigned)); - PJ_ASSERT_RETURN(conf_port->listener_adj_level, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port->listener_adj_level, + {status = PJ_ENOMEM; goto on_return;}); /* Save some port's infos, for convenience. */ if (port) { @@ -315,6 +461,8 @@ static pj_status_t create_conf_port( pj_pool_t *pool, /* Create adjustment level buffer. */ conf_port->adj_level_buf = (pj_int16_t*) pj_pool_zalloc(pool, conf->samples_per_frame * sizeof(pj_int16_t)); + PJ_ASSERT_ON_FAIL(conf_port->adj_level_buf, + {status = PJ_ENOMEM; goto on_return;}); /* If port's clock rate is different than conference's clock rate, * create a resample sessions. @@ -339,7 +487,7 @@ static pj_status_t create_conf_port( pj_pool_t *pool, conf->clock_rate, &conf_port->rx_resample); if (status != PJ_SUCCESS) - return status; + goto on_return; /* Create resample for tx buffer. */ @@ -352,7 +500,7 @@ static pj_status_t create_conf_port( pj_pool_t *pool, conf->samples_per_frame, &conf_port->tx_resample); if (status != PJ_SUCCESS) - return status; + goto on_return; } /* @@ -403,7 +551,8 @@ static pj_status_t create_conf_port( pj_pool_t *pool, conf_port->rx_buf = (pj_int16_t*) pj_pool_alloc(pool, conf_port->rx_buf_cap * sizeof(conf_port->rx_buf[0])); - PJ_ASSERT_RETURN(conf_port->rx_buf, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port->rx_buf, + {status = PJ_ENOMEM; goto on_return;}); /* Create TX buffer. */ conf_port->tx_buf_cap = conf_port->rx_buf_cap; @@ -411,7 +560,8 @@ static pj_status_t create_conf_port( pj_pool_t *pool, conf_port->tx_buf = (pj_int16_t*) pj_pool_alloc(pool, conf_port->tx_buf_cap * sizeof(conf_port->tx_buf[0])); - PJ_ASSERT_RETURN(conf_port->tx_buf, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port->tx_buf, + {status = PJ_ENOMEM; goto on_return;}); } @@ -419,13 +569,21 @@ static pj_status_t create_conf_port( pj_pool_t *pool, conf_port->mix_buf = (pj_int32_t*) pj_pool_zalloc(pool, conf->samples_per_frame * sizeof(conf_port->mix_buf[0])); - PJ_ASSERT_RETURN(conf_port->mix_buf, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(conf_port->mix_buf, + {status = PJ_ENOMEM; goto on_return;}); conf_port->last_mix_adj = NORMAL_LEVEL; /* Done */ *p_conf_port = conf_port; - return PJ_SUCCESS; + +on_return: + if (status != PJ_SUCCESS) { + if (pool) + pj_pool_release(pool); + } + + return status; } @@ -538,7 +696,7 @@ static pj_status_t create_sound_port( pj_pool_t *pool, /* * Create conference bridge. */ -PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool_, unsigned max_ports, unsigned clock_rate, unsigned channel_count, @@ -547,6 +705,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, unsigned options, pjmedia_conf **p_conf ) { + pj_pool_t *pool; pjmedia_conf *conf; const pj_str_t name = { "Conf", 4 }; pj_status_t status; @@ -558,9 +717,17 @@ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports", max_ports)); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, name.ptr, 512, 512, NULL); + if (!pool) { + PJ_PERROR(1, (THIS_FILE, PJ_ENOMEM, "Create failed in alloc")); + return PJ_ENOMEM; + } + /* Create and init conf structure. */ conf = PJ_POOL_ZALLOC_T(pool, pjmedia_conf); PJ_ASSERT_RETURN(conf, PJ_ENOMEM); + conf->pool = pool; conf->ports = (struct conf_port**) pj_pool_zalloc(pool, max_ports*sizeof(void*)); @@ -617,6 +784,17 @@ PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool, } + /* Allocate synchronized operation queues */ + conf->op_queue = PJ_POOL_ZALLOC_T(pool, op_entry); + conf->op_queue_free = PJ_POOL_ZALLOC_T(pool, op_entry); + if (!conf->op_queue || !conf->op_queue_free) { + PJ_PERROR(1, (THIS_FILE, PJ_ENOMEM, "Create failed in create queues")); + pjmedia_conf_destroy(conf); + return PJ_ENOMEM; + } + pj_list_init(conf->op_queue); + pj_list_init(conf->op_queue_free); + /* Done */ *p_conf = conf; @@ -651,37 +829,27 @@ static pj_status_t resume_sound( pjmedia_conf *conf ) */ PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf ) { - unsigned i, ci; + unsigned i; PJ_ASSERT_RETURN(conf != NULL, PJ_EINVAL); + pj_log_push_indent(); + /* Destroy sound device port. */ if (conf->snd_dev_port) { pjmedia_snd_port_destroy(conf->snd_dev_port); conf->snd_dev_port = NULL; } - /* Destroy delay buf of all (passive) ports. */ - for (i=0, ci=0; imax_ports && ciport_cnt; ++i) { - struct conf_port *cport; + /* Flush any pending operation (connect, disconnect, etc) */ + handle_op_queue(conf); - cport = conf->ports[i]; - if (!cport) - continue; - - ++ci; - - if (cport->rx_resample) { - pjmedia_resample_destroy(cport->rx_resample); - cport->rx_resample = NULL; - } - if (cport->tx_resample) { - pjmedia_resample_destroy(cport->tx_resample); - cport->tx_resample = NULL; - } - if (cport->delay_buf) { - pjmedia_delay_buf_destroy(cport->delay_buf); - cport->delay_buf = NULL; + /* Remove all ports (may destroy them too). */ + for (i=0; imax_ports; ++i) { + if (conf->ports[i]) { + op_param oprm = {0}; + oprm.remove_port.port = i; + op_remove_port(conf, &oprm); } } @@ -689,6 +857,12 @@ PJ_DEF(pj_status_t) pjmedia_conf_destroy( pjmedia_conf *conf ) if (conf->mutex) pj_mutex_destroy(conf->mutex); + /* Destroy pool */ + if (conf->pool) + pj_pool_safe_release(&conf->pool); + + pj_log_pop_indent(); + return PJ_SUCCESS; } @@ -770,10 +944,12 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf, { struct conf_port *conf_port; unsigned index; - pj_status_t status; + pj_status_t status = PJ_SUCCESS; PJ_ASSERT_RETURN(conf && pool && strm_port, PJ_EINVAL); + pj_log_push_indent(); + /* If port_name is not specified, use the port's name */ if (!port_name) port_name = &strm_port->info.name; @@ -787,44 +963,52 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_port( pjmedia_conf *conf, conf->channel_count != 1)) { pj_assert(!"Number of channels mismatch"); - return PJMEDIA_ENCCHANNEL; + status = PJMEDIA_ENCCHANNEL; + goto on_return; } pj_mutex_lock(conf->mutex); - if (conf->port_cnt >= conf->max_ports) { - pj_assert(!"Too many ports"); - pj_mutex_unlock(conf->mutex); - return PJ_ETOOMANY; - } - /* Find empty port in the conference bridge. */ for (index=0; index < conf->max_ports; ++index) { if (conf->ports[index] == NULL) break; } - pj_assert(index != conf->max_ports); + if (index == conf->max_ports) { + PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY, "Add port %s failed", + port_name->ptr)); + status = PJ_ETOOMANY; + goto on_return; + } /* Create conf port structure. */ status = create_conf_port(pool, conf, strm_port, port_name, &conf_port); - if (status != PJ_SUCCESS) { - pj_mutex_unlock(conf->mutex); - return status; - } + if (status != PJ_SUCCESS) + goto on_return; - /* Put the port. */ + /* Audio data flow is not protected, avoid processing this newly + * added port. + */ + conf_port->is_new = PJ_TRUE; + + /* Put the port, but don't add port counter yet */ conf->ports[index] = conf_port; - conf->port_cnt++; + //conf->port_cnt++; /* Done. */ if (p_port) { *p_port = index; } + PJ_LOG(5,(THIS_FILE, "Adding new port %d (%.*s)", + index, (int)port_name->slen, port_name->ptr)); + +on_return: pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); - return PJ_SUCCESS; + return status; } @@ -983,7 +1167,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, unsigned i; /* Check arguments */ - PJ_ASSERT_RETURN(conf && src_slotmax_ports && + PJ_ASSERT_RETURN(conf && src_slotmax_ports && sink_slotmax_ports, PJ_EINVAL); /* Value must be from -128 to +127 */ @@ -992,43 +1176,52 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, */ PJ_ASSERT_RETURN(adj_level >= -128, PJ_EINVAL); + pj_log_push_indent(); + + PJ_LOG(5,(THIS_FILE, "Connect ports %d->%d requested", + src_slot, sink_slot)); + pj_mutex_lock(conf->mutex); /* Ports must be valid. */ src_port = conf->ports[src_slot]; dst_port = conf->ports[sink_slot]; if (!src_port || !dst_port) { + PJ_PERROR(3,(THIS_FILE, PJ_EINVAL, + "Failed connecting ports, make sure ports are valid")); pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); return PJ_EINVAL; } /* Check if connection has been made */ for (i=0; ilistener_cnt; ++i) { - if (src_port->listener_slots[i] == sink_slot) + if (src_port->listener_slots[i] == sink_slot) { + PJ_LOG(3,(THIS_FILE, "Ports connection %d->%d already exists", + src_slot, sink_slot)); break; + } } + /* Queue the operation */ if (i == src_port->listener_cnt) { - src_port->listener_slots[src_port->listener_cnt] = sink_slot; - /* Set normalized adjustment level. */ - src_port->listener_adj_level[src_port->listener_cnt] = adj_level + - NORMAL_LEVEL; - ++conf->connect_cnt; - ++src_port->listener_cnt; - ++dst_port->transmitter_cnt; - - if (conf->connect_cnt == 1) - start_sound = 1; - - PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)", - src_slot, - (int)src_port->name.slen, - src_port->name.ptr, - sink_slot, - (int)dst_port->name.slen, - dst_port->name.ptr)); + op_entry *ope; + + ope = get_free_op_entry(conf); + ope->type = OP_CONNECT_PORTS; + ope->param.connect_ports.src = src_slot; + ope->param.connect_ports.sink = sink_slot; + ope->param.connect_ports.adj_level = adj_level; + pj_list_push_back(conf->op_queue, ope); + + PJ_LOG(4,(THIS_FILE, "Connect ports %d->%d queued", + src_slot, sink_slot)); } + /* This is first connection, start clock */ + if (conf->connect_cnt == 0) + start_sound = 1; + pj_mutex_unlock(conf->mutex); /* Sound device must be started without mutex, otherwise the @@ -1037,9 +1230,56 @@ PJ_DEF(pj_status_t) pjmedia_conf_connect_port( pjmedia_conf *conf, if (start_sound) resume_sound(conf); + pj_log_pop_indent(); + return PJ_SUCCESS; } +static void op_connect_ports(pjmedia_conf *conf, const op_param *prm) +{ + unsigned src_slot, sink_slot; + struct conf_port *src_port, *dst_port; + unsigned i; + + /* Ports must be valid. */ + src_slot = prm->connect_ports.src; + sink_slot = prm->connect_ports.sink; + src_port = conf->ports[src_slot]; + dst_port = conf->ports[sink_slot]; + + if (!src_port || !dst_port) { + PJ_PERROR(3,(THIS_FILE, PJ_EINVAL, + "Failed connecting ports, make sure ports are valid")); + return; + } + + /* Check if connection has been made */ + for (i=0; ilistener_cnt; ++i) { + if (src_port->listener_slots[i] == sink_slot) { + PJ_LOG(3,(THIS_FILE, "Ports connection %d->%d already exists", + src_slot, sink_slot)); + return; + } + } + + src_port->listener_slots[src_port->listener_cnt] = sink_slot; + + /* Set normalized adjustment level. */ + src_port->listener_adj_level[src_port->listener_cnt] = + prm->connect_ports.adj_level + NORMAL_LEVEL; + + ++conf->connect_cnt; + ++src_port->listener_cnt; + ++dst_port->transmitter_cnt; + + PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)", + src_slot, + (int)src_port->name.slen, + src_port->name.ptr, + sink_slot, + (int)dst_port->name.slen, + dst_port->name.ptr)); +} /* * Disconnect port @@ -1049,20 +1289,26 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, unsigned sink_slot ) { struct conf_port *src_port, *dst_port; - pj_bool_t no_conn = PJ_FALSE; unsigned i; /* Check arguments */ - PJ_ASSERT_RETURN(conf && src_slotmax_ports && + PJ_ASSERT_RETURN(conf && src_slotmax_ports && sink_slotmax_ports, PJ_EINVAL); + pj_log_push_indent(); + + PJ_LOG(5,(THIS_FILE, "Disconnect ports %d->%d requested", + src_slot, sink_slot)); + pj_mutex_lock(conf->mutex); /* Ports must be valid. */ src_port = conf->ports[src_slot]; dst_port = conf->ports[sink_slot]; if (!src_port || !dst_port) { + PJ_PERROR(3,(THIS_FILE, PJ_EINVAL,"Cannot disconnect invalid ports")); pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); return PJ_EINVAL; } @@ -1072,12 +1318,62 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, break; } - if (i != src_port->listener_cnt) { - pj_assert(src_port->listener_cnt > 0 && + if (i == src_port->listener_cnt) { + PJ_LOG(3,(THIS_FILE, "Ports connection %d->%d does not exist", + src_slot, sink_slot)); + } else { + op_entry *ope; + + ope = get_free_op_entry(conf); + ope->type = OP_DISCONNECT_PORTS; + ope->param.disconnect_ports.src = src_slot; + ope->param.disconnect_ports.sink = sink_slot; + pj_list_push_back(conf->op_queue, ope); + + PJ_LOG(4,(THIS_FILE, "Disconnect ports %d->%d queued", + src_slot, sink_slot)); + } + + pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); + + return PJ_SUCCESS; +} + +static void op_disconnect_ports(pjmedia_conf *conf, + const op_param *prm) +{ + unsigned src_slot, sink_slot; + struct conf_port *src_port = NULL, *dst_port = NULL; + unsigned i; + + /* Ports must be valid. */ + src_slot = prm->disconnect_ports.src; + sink_slot = prm->disconnect_ports.sink; + + if (src_slot != INVALID_SLOT) + src_port = conf->ports[src_slot]; + if (sink_slot != INVALID_SLOT) + dst_port = conf->ports[sink_slot]; + + /* Disconnect source -> sink */ + if (src_port && dst_port) { + /* Check if connection has been made */ + for (i=0; ilistener_cnt; ++i) { + if (src_port->listener_slots[i] == sink_slot) + break; + } + if (i == src_port->listener_cnt) { + PJ_LOG(3,(THIS_FILE, "Ports connection %d->%d does not exist", + src_slot, sink_slot)); + return; + } + + pj_assert(src_port->listener_cnt > 0 && src_port->listener_cnt < conf->max_ports); - pj_assert(dst_port->transmitter_cnt > 0 && + pj_assert(dst_port->transmitter_cnt > 0 && dst_port->transmitter_cnt < conf->max_ports); - pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), + pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), src_port->listener_cnt, i); pj_array_erase(src_port->listener_adj_level, sizeof(unsigned), src_port->listener_cnt, i); @@ -1094,24 +1390,68 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf, (int)dst_port->name.slen, dst_port->name.ptr)); - /* if source port is passive port and has no listener, reset delaybuf */ + /* if source port is passive port and has no listener, + * reset delaybuf. + */ if (src_port->delay_buf && src_port->listener_cnt == 0) pjmedia_delay_buf_reset(src_port->delay_buf); - } - /* Evaluate connect_cnt with mutex, but pause sound dev outside mutex */ - no_conn = (conf->connect_cnt == 0); + /* Disconnect multiple conn: any -> sink */ + } else if (dst_port) { + PJ_LOG(4,(THIS_FILE, + "Stop any transmission to port %d (%.*s)", + sink_slot, + (int)dst_port->name.slen, + dst_port->name.ptr)); - pj_mutex_unlock(conf->mutex); + for (i=0; imax_ports; ++i) { + unsigned j; - if (no_conn) { - pause_sound(conf); + src_port = conf->ports[i]; + if (!src_port || src_port->listener_cnt == 0) + continue; + + for (j=0; jlistener_cnt; ++j) { + if (src_port->listener_slots[j] == sink_slot) { + op_param op_prm = {0}; + op_prm.disconnect_ports.src = i; + op_prm.disconnect_ports.sink = sink_slot; + op_disconnect_ports(conf, &op_prm); + break; + } + } + } + + /* Disconnect multiple conn: source -> any */ + } else if (src_port) { + PJ_LOG(4,(THIS_FILE, + "Stop any transmission from port %d (%.*s)", + src_slot, + (int)src_port->name.slen, + src_port->name.ptr)); + + for (i=0; ilistener_cnt; ++i) { + op_param op_prm = {0}; + op_prm.disconnect_ports.src = src_slot; + op_prm.disconnect_ports.sink = src_port->listener_slots[i]; + op_disconnect_ports(conf, &op_prm); + } + + /* Invalid ports */ + } else { + pj_assert(!"Invalid ports specified in conf disconnect"); } - return PJ_SUCCESS; + /* Pause sound dev when there is no connection, the pause should be done + * outside mutex to avoid possible deadlock. + * Note that currently this is done with mutex, it is safe because + * pause_sound() is a no-op (just maintaining old code). + */ + if (conf->connect_cnt == 0) { + pause_sound(conf); + } } - /* * Disconnect port from all sources */ @@ -1119,45 +1459,43 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port_from_sources( pjmedia_conf *conf, unsigned sink_slot) { - unsigned i; + struct conf_port *dst_port; /* Check arguments */ PJ_ASSERT_RETURN(conf && sink_slotmax_ports, PJ_EINVAL); - pj_mutex_lock(conf->mutex); + pj_log_push_indent(); + PJ_LOG(5,(THIS_FILE, "Disconnect ports any->%d requested", + sink_slot)); - /* Remove this port from transmit array of other ports. */ - for (i=0; imax_ports; ++i) { - unsigned j; - struct conf_port *src_port; - - src_port = conf->ports[i]; + pj_mutex_lock(conf->mutex); - if (!src_port) - continue; + /* Ports must be valid. */ + dst_port = conf->ports[sink_slot]; + if (!dst_port) { + PJ_PERROR(3,(THIS_FILE, PJ_EINVAL,"Cannot disconnect invalid port")); + pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); + return PJ_EINVAL; + } - if (src_port->listener_cnt == 0) - continue; + if (dst_port->transmitter_cnt == 0) { + PJ_LOG(3,(THIS_FILE, "Port %d does not have any transmitter", + sink_slot)); + } else { + op_entry *ope; - for (j=0; jlistener_cnt; ++j) { - if (src_port->listener_slots[j] == sink_slot) { - pj_array_erase(src_port->listener_slots, sizeof(SLOT_TYPE), - src_port->listener_cnt, j); - pj_array_erase(src_port->listener_adj_level, sizeof(unsigned), - src_port->listener_cnt, j); - pj_assert(conf->connect_cnt > 0); - --conf->connect_cnt; - --src_port->listener_cnt; - break; - } - } - } + ope = get_free_op_entry(conf); + ope->type = OP_DISCONNECT_PORTS; + ope->param.disconnect_ports.src = INVALID_SLOT; + ope->param.disconnect_ports.sink = sink_slot; + pj_list_push_back(conf->op_queue, ope); - if (conf->connect_cnt == 0) { - pause_sound(conf); + PJ_LOG(4,(THIS_FILE, "Disconnect ports any->%d queued", sink_slot)); } pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); return PJ_SUCCESS; } @@ -1175,33 +1513,38 @@ pjmedia_conf_disconnect_port_from_sinks( pjmedia_conf *conf, /* Check arguments */ PJ_ASSERT_RETURN(conf && src_slotmax_ports, PJ_EINVAL); + pj_log_push_indent(); + + PJ_LOG(5,(THIS_FILE, "Disconnect ports %d->any requested", + src_slot)); + pj_mutex_lock(conf->mutex); /* Port must be valid. */ src_port = conf->ports[src_slot]; if (!src_port) { + PJ_PERROR(3,(THIS_FILE, PJ_EINVAL,"Cannot disconnect invalid port")); pj_mutex_unlock(conf->mutex); return PJ_EINVAL; } - /* Update transmitter_cnt of ports we're transmitting to */ - while (src_port->listener_cnt) { - unsigned dst_slot; - struct conf_port *dst_port; + if (src_port->listener_cnt == 0) { + PJ_LOG(3,(THIS_FILE, "Port %d does not have any transmitter", + src_slot)); + } else { + op_entry *ope; - dst_slot = src_port->listener_slots[src_port->listener_cnt-1]; - dst_port = conf->ports[dst_slot]; - --dst_port->transmitter_cnt; - --src_port->listener_cnt; - pj_assert(conf->connect_cnt > 0); - --conf->connect_cnt; - } + ope = get_free_op_entry(conf); + ope->type = OP_DISCONNECT_PORTS; + ope->param.disconnect_ports.src = src_slot; + ope->param.disconnect_ports.sink = INVALID_SLOT; + pj_list_push_back(conf->op_queue, ope); - if (conf->connect_cnt == 0) { - pause_sound(conf); + PJ_LOG(4,(THIS_FILE, "Disconnect ports %d->any queued", src_slot)); } pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); return PJ_SUCCESS; } @@ -1231,32 +1574,67 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, unsigned port ) { struct conf_port *conf_port; + op_entry *ope; - /* Check arguments */ - PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL); + pj_log_push_indent(); - /* Suspend the sound devices. - * Don't want to remove port while port is being accessed by sound - * device's threads! - */ + PJ_LOG(5,(THIS_FILE, "Port %d remove requested", port)); + + PJ_ASSERT_RETURN(conf && port < conf->max_ports, PJ_EINVAL); pj_mutex_lock(conf->mutex); /* Port must be valid. */ conf_port = conf->ports[port]; if (conf_port == NULL) { + PJ_PERROR(3, (THIS_FILE, PJ_EINVAL, "Remove port failed")); pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); return PJ_EINVAL; } + /* Queue the operation */ + ope = get_free_op_entry(conf); + ope->type = OP_REMOVE_PORT; + ope->param.remove_port.port = port; + pj_list_push_back(conf->op_queue, ope); + PJ_LOG(4,(THIS_FILE, "Port %d (%.*s) remove queued", port, + (int)conf_port->name.slen, conf_port->name.ptr)); + + pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); + + return PJ_SUCCESS; +} + + +static void op_remove_port(pjmedia_conf *conf, const op_param *prm) +{ + unsigned port = prm->remove_port.port; + struct conf_port *conf_port; + op_param op_prm; + + /* Port must be valid. */ + conf_port = conf->ports[port]; + if (conf_port == NULL) { + PJ_PERROR(3, (THIS_FILE, PJ_ENOTFOUND, "Remove port failed")); + return; + } + conf_port->tx_setting = PJMEDIA_PORT_DISABLE; conf_port->rx_setting = PJMEDIA_PORT_DISABLE; /* disconnect port from all sources which are transmitting to it */ - pjmedia_conf_disconnect_port_from_sources(conf, port); + pj_bzero(&op_prm, sizeof(op_prm)); + op_prm.disconnect_ports.src = INVALID_SLOT; + op_prm.disconnect_ports.sink = port; + op_disconnect_ports(conf, &op_prm); /* disconnect port from all sinks to which it is transmitting to */ - pjmedia_conf_disconnect_port_from_sinks(conf, port); + pj_bzero(&op_prm, sizeof(op_prm)); + op_prm.disconnect_ports.src = port; + op_prm.disconnect_ports.sink = INVALID_SLOT; + op_disconnect_ports(conf, &op_prm); /* Destroy resample if this conf port has it. */ if (conf_port->rx_resample) { @@ -1275,7 +1653,8 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, pjmedia_delay_buf_destroy(conf_port->delay_buf); conf_port->delay_buf = NULL; - pjmedia_port_destroy(conf_port->port); + if (conf_port->port) + pjmedia_port_destroy(conf_port->port); conf_port->port = NULL; } @@ -1283,9 +1662,14 @@ PJ_DEF(pj_status_t) pjmedia_conf_remove_port( pjmedia_conf *conf, conf->ports[port] = NULL; --conf->port_cnt; - pj_mutex_unlock(conf->mutex); + PJ_LOG(4,(THIS_FILE,"Removed port %d (%.*s)", + port, (int)conf_port->name.slen, conf_port->name.ptr)); - return PJ_SUCCESS; + /* Decrease conf port ref count */ + if (conf_port->port && conf_port->port->grp_lock) + pj_grp_lock_dec_ref(conf_port->port->grp_lock); + else + conf_port_on_destroy(conf_port); } @@ -1964,8 +2348,38 @@ static pj_status_t get_frame(pjmedia_port *this_port, pj_assert(frame->size == conf->samples_per_frame * conf->bits_per_sample / 8); - /* Must lock mutex */ - pj_mutex_lock(conf->mutex); + /* Perform any queued operations that need to be synchronized with + * the clock such as connect, disonnect, remove. + */ + if (!pj_list_empty(conf->op_queue)) { + pj_log_push_indent(); + pj_mutex_lock(conf->mutex); + + /* Activate any newly added port */ + for (i=0; imax_ports; ++i) { + struct conf_port *port = conf->ports[i]; + if (!port || !port->is_new) + continue; + + port->is_new = PJ_FALSE; + ++conf->port_cnt; + + PJ_LOG(5,(THIS_FILE, "New port %d (%.*s) is added", + i, (int)port->name.slen, port->name.ptr)); + } + + handle_op_queue(conf); + + pj_mutex_unlock(conf->mutex); + pj_log_pop_indent(); + } + + /* No mutex from this point! Otherwise it may cause deadlock as + * put_frame()/get_frame() may invoke callback. + * + * Note that any changes on the conference connections must be + * synchronized. + */ /* Reset port source count. We will only reset port's mix * buffer when we have someone transmitting to it. @@ -1973,8 +2387,8 @@ static pj_status_t get_frame(pjmedia_port *this_port, for (i=0, ci=0; imax_ports && ci < conf->port_cnt; ++i) { struct conf_port *conf_port = conf->ports[i]; - /* Skip empty port. */ - if (!conf_port) + /* Skip empty or new port. */ + if (!conf_port || conf_port->is_new) continue; /* Var "ci" is to count how many ports have been visited so far. */ @@ -2001,8 +2415,8 @@ static pj_status_t get_frame(pjmedia_port *this_port, struct conf_port *conf_port = conf->ports[i]; pj_int32_t level = 0; - /* Skip empty port. */ - if (!conf_port) + /* Skip empty or new port. */ + if (!conf_port || conf_port->is_new) continue; /* Var "ci" is to count how many ports have been visited so far. */ @@ -2216,7 +2630,7 @@ static pj_status_t get_frame(pjmedia_port *this_port, pjmedia_frame_type frm_type; pj_status_t status; - if (!conf_port) + if (!conf_port || conf_port->is_new) continue; /* Var "ci" is to count how many ports have been visited. */ @@ -2263,8 +2677,6 @@ static pj_status_t get_frame(pjmedia_port *this_port, /* MUST set frame type */ frame->type = speaker_frame_type; - pj_mutex_unlock(conf->mutex); - #ifdef REC_FILE if (fhnd_rec == NULL) fhnd_rec = fopen(REC_FILE, "wb"); diff --git a/pjmedia/src/pjmedia/echo_port.c b/pjmedia/src/pjmedia/echo_port.c index 5fc24bf556..23741ffefe 100644 --- a/pjmedia/src/pjmedia/echo_port.c +++ b/pjmedia/src/pjmedia/echo_port.c @@ -31,6 +31,7 @@ struct ec { pjmedia_port base; + pj_pool_t *pool; pjmedia_port *dn_port; pjmedia_echo_state *ec; }; @@ -43,7 +44,7 @@ static pj_status_t ec_get_frame(pjmedia_port *this_port, static pj_status_t ec_on_destroy(pjmedia_port *this_port); -PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool_, pjmedia_port *dn_port, unsigned tail_ms, unsigned latency_ms, @@ -54,16 +55,22 @@ PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool, pjmedia_audio_format_detail *afd; struct ec *ec; pj_status_t status; + pj_pool_t *pool = NULL; - PJ_ASSERT_RETURN(pool && dn_port && p_port, PJ_EINVAL); + PJ_ASSERT_RETURN(pool_ && dn_port && p_port, PJ_EINVAL); afd = pjmedia_format_get_audio_format_detail(&dn_port->info.fmt, PJ_TRUE); PJ_ASSERT_RETURN(afd->bits_per_sample==16 && tail_ms, PJ_EINVAL); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, AEC.ptr, 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + /* Create the port and the AEC itself */ ec = PJ_POOL_ZALLOC_T(pool, struct ec); + ec->pool = pool; pjmedia_port_info_init(&ec->base.info, &AEC, SIGNATURE, afd->clock_rate, @@ -75,8 +82,10 @@ PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool, afd->channel_count, PJMEDIA_AFD_SPF(afd), tail_ms, latency_ms, options, &ec->ec); - if (status != PJ_SUCCESS) + if (status != PJ_SUCCESS) { + pj_pool_release(pool); return status; + } /* More init */ ec->dn_port = dn_port; @@ -84,6 +93,9 @@ PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool, ec->base.put_frame = &ec_put_frame; ec->base.on_destroy = &ec_on_destroy; + if (dn_port->grp_lock) + pjmedia_port_init_grp_lock(&ec->base, pool, dn_port->grp_lock); + /* Done */ *p_port = &ec->base; @@ -137,7 +149,13 @@ static pj_status_t ec_on_destroy(pjmedia_port *this_port) PJ_ASSERT_RETURN(this_port->info.signature == SIGNATURE, PJ_EINVAL); - pjmedia_echo_destroy(ec->ec); + if (ec->ec) { + pjmedia_echo_destroy(ec->ec); + ec->ec = NULL; + } + + if (ec->pool) + pj_pool_safe_release(&ec->pool); return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/mem_capture.c b/pjmedia/src/pjmedia/mem_capture.c index d03771c0ad..679011b047 100644 --- a/pjmedia/src/pjmedia/mem_capture.c +++ b/pjmedia/src/pjmedia/mem_capture.c @@ -31,6 +31,7 @@ struct mem_rec { pjmedia_port base; + pj_pool_t *pool; unsigned options; @@ -54,7 +55,7 @@ static pj_status_t rec_get_frame(pjmedia_port *this_port, static pj_status_t rec_on_destroy(pjmedia_port *this_port); -PJ_DEF(pj_status_t) pjmedia_mem_capture_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_mem_capture_create( pj_pool_t *pool_, void *buffer, pj_size_t size, unsigned clock_rate, @@ -66,18 +67,26 @@ PJ_DEF(pj_status_t) pjmedia_mem_capture_create( pj_pool_t *pool, { struct mem_rec *rec; const pj_str_t name = { "memrec", 6 }; + pj_pool_t *pool; /* Sanity check */ - PJ_ASSERT_RETURN(pool && buffer && size && clock_rate && channel_count && + PJ_ASSERT_RETURN(pool_ && buffer && size && clock_rate && channel_count && samples_per_frame && bits_per_sample && p_port, PJ_EINVAL); /* Can only support 16bit PCM */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, "memcap", 128, 128, NULL); + if (!pool) { + PJ_PERROR(1, (THIS_FILE, PJ_ENOMEM, "Mem capture create failed")); + return PJ_ENOMEM; + } rec = PJ_POOL_ZALLOC_T(pool, struct mem_rec); - PJ_ASSERT_RETURN(rec != NULL, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(rec != NULL, {pj_pool_release(pool); return PJ_ENOMEM;}); + rec->pool = pool; /* Create the rec */ pjmedia_port_info_init(&rec->base.info, &name, SIGNATURE, @@ -298,6 +307,9 @@ static pj_status_t rec_on_destroy(pjmedia_port *this_port) (*rec->cb)(this_port, rec->user_data); } + if (rec->pool) + pj_pool_safe_release(&rec->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/mem_player.c b/pjmedia/src/pjmedia/mem_player.c index ff38065df6..1570947522 100644 --- a/pjmedia/src/pjmedia/mem_player.c +++ b/pjmedia/src/pjmedia/mem_player.c @@ -31,6 +31,7 @@ struct mem_player { pjmedia_port base; + pj_pool_t *pool; unsigned options; pj_timestamp timestamp; @@ -55,7 +56,7 @@ static pj_status_t mem_get_frame(pjmedia_port *this_port, static pj_status_t mem_on_destroy(pjmedia_port *this_port); -PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool_, const void *buffer, pj_size_t size, unsigned clock_rate, @@ -67,9 +68,10 @@ PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool, { struct mem_player *port; pj_str_t name = pj_str("memplayer"); + pj_pool_t *pool = NULL; /* Sanity check */ - PJ_ASSERT_RETURN(pool && buffer && size && clock_rate && channel_count && + PJ_ASSERT_RETURN(pool_ && buffer && size && clock_rate && channel_count && samples_per_frame && bits_per_sample && p_port, PJ_EINVAL); @@ -77,8 +79,13 @@ PJ_DEF(pj_status_t) pjmedia_mem_player_create( pj_pool_t *pool, PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, name.ptr, 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + port = PJ_POOL_ZALLOC_T(pool, struct mem_player); - PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(port != NULL, {pj_pool_release(pool);return PJ_ENOMEM;}); + port->pool = pool; /* Create the port */ pjmedia_port_info_init(&port->base.info, &name, SIGNATURE, clock_rate, @@ -318,6 +325,9 @@ static pj_status_t mem_on_destroy(pjmedia_port *this_port) /* Destroy signature */ this_port->info.signature = 0; + if (player->pool) + pj_pool_safe_release(&player->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/null_port.c b/pjmedia/src/pjmedia/null_port.c index fb5dfccf4d..92bc813faf 100644 --- a/pjmedia/src/pjmedia/null_port.c +++ b/pjmedia/src/pjmedia/null_port.c @@ -25,6 +25,12 @@ #define SIGNATURE PJMEDIA_SIG_PORT_NULL +struct null_port +{ + pjmedia_port base; + pj_pool_t *pool; +}; + static pj_status_t null_get_frame(pjmedia_port *this_port, pjmedia_frame *frame); static pj_status_t null_put_frame(pjmedia_port *this_port, @@ -32,30 +38,36 @@ static pj_status_t null_put_frame(pjmedia_port *this_port, static pj_status_t null_on_destroy(pjmedia_port *this_port); -PJ_DEF(pj_status_t) pjmedia_null_port_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_null_port_create( pj_pool_t *pool_, unsigned sampling_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjmedia_port **p_port ) { - pjmedia_port *port; + struct null_port *port; const pj_str_t name = pj_str("null-port"); + pj_pool_t *pool; + + PJ_ASSERT_RETURN(pool_ && p_port, PJ_EINVAL); - PJ_ASSERT_RETURN(pool && p_port, PJ_EINVAL); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, name.ptr, 128, 128, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); - port = PJ_POOL_ZALLOC_T(pool, pjmedia_port); - PJ_ASSERT_RETURN(port != NULL, PJ_ENOMEM); + port = PJ_POOL_ZALLOC_T(pool, struct null_port); + PJ_ASSERT_ON_FAIL(port, {pj_pool_release(pool); return PJ_ENOMEM;}); + port->pool = pool; - pjmedia_port_info_init(&port->info, &name, SIGNATURE, sampling_rate, + pjmedia_port_info_init(&port->base.info, &name, SIGNATURE, sampling_rate, channel_count, bits_per_sample, samples_per_frame); - port->get_frame = &null_get_frame; - port->put_frame = &null_put_frame; - port->on_destroy = &null_on_destroy; + port->base.get_frame = &null_get_frame; + port->base.put_frame = &null_put_frame; + port->base.on_destroy = &null_on_destroy; - *p_port = port; + *p_port = &port->base; return PJ_SUCCESS; } @@ -95,6 +107,10 @@ static pj_status_t null_get_frame(pjmedia_port *this_port, */ static pj_status_t null_on_destroy(pjmedia_port *this_port) { - PJ_UNUSED_ARG(this_port); + struct null_port* port = (struct null_port*) this_port; + + if (port->pool) + pj_pool_safe_release(&port->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/port.c b/pjmedia/src/pjmedia/port.c index 605e060bbc..cede0d0688 100644 --- a/pjmedia/src/pjmedia/port.c +++ b/pjmedia/src/pjmedia/port.c @@ -126,8 +126,7 @@ PJ_DEF(pj_status_t) pjmedia_port_destroy( pjmedia_port *port ) PJ_ASSERT_RETURN(port, PJ_EINVAL); if (port->grp_lock) { - pjmedia_port_dec_ref(port); - return PJ_SUCCESS; + return pjmedia_port_dec_ref(port); } if (port->on_destroy) { @@ -160,16 +159,17 @@ PJ_DEF(pj_status_t) pjmedia_port_init_grp_lock( pjmedia_port *port, PJ_ASSERT_RETURN(port && pool, PJ_EINVAL); PJ_ASSERT_RETURN(port->grp_lock == NULL, PJ_EEXISTS); - /* We need to be caution on ports that do not have the on_destroy()! - * It is either uninitialized yet or the port does not have one. - * If the port doesn't have one, we'd expect a possible premature destroy! + /* We need to be caution on ports that do not have its own pool, + * such port is likely using app's pool, so if the app destroys the port + * and then destroys the pool immediately, it may cause crash as the port + * may have not really been destroyed and may still be accessed. + * When port has a pool, it usually implements on_destroy() for releasing + * the pool, so here we check availability of on_destroy implementation. */ if (port->on_destroy == NULL) { - PJ_LOG(3,(THIS_FILE, "Media port %s is using group lock but does not " - "implement on_destroy()!", + PJ_LOG(2,(THIS_FILE, "Warning, media port %s is using group lock, but " + "it does not seem to have a pool.", port->info.name.ptr)); - pj_assert(!"Port using group lock should implement on_destroy()!"); - return PJ_EINVALIDOP; } if (!grp_lock) { @@ -177,14 +177,19 @@ PJ_DEF(pj_status_t) pjmedia_port_init_grp_lock( pjmedia_port *port, status = pj_grp_lock_create_w_handler(pool, NULL, port, &port_on_destroy, &grp_lock); - } else { - /* Just add handler, and use internal group lock pool */ - status = pj_grp_lock_add_handler(grp_lock, NULL, port, - &port_on_destroy); - } - if (status == PJ_SUCCESS) { + /* Add ref */ + if (status == PJ_SUCCESS) + status = pj_grp_lock_add_ref(grp_lock); + } else { + /* Add ref first before add handler */ status = pj_grp_lock_add_ref(grp_lock); + + /* Just add handler, and use internal group lock pool */ + if (status == PJ_SUCCESS) { + status = pj_grp_lock_add_handler(grp_lock, NULL, port, + &port_on_destroy); + } } if (status == PJ_SUCCESS) { diff --git a/pjmedia/src/pjmedia/splitcomb.c b/pjmedia/src/pjmedia/splitcomb.c index fd03ec94d5..b66774a840 100644 --- a/pjmedia/src/pjmedia/splitcomb.c +++ b/pjmedia/src/pjmedia/splitcomb.c @@ -83,6 +83,7 @@ enum sc_dir struct splitcomb { pjmedia_port base; + pj_pool_t *pool; unsigned options; @@ -203,7 +204,7 @@ static pj_status_t rport_on_destroy(pjmedia_port *this_port); /* * Create the splitter/combiner. */ -PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool_, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, @@ -212,10 +213,12 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool, pjmedia_port **p_splitcomb) { const pj_str_t name = pj_str("splitcomb"); + pj_pool_t *pool = NULL; struct splitcomb *sc; + pj_status_t status; /* Sanity check */ - PJ_ASSERT_RETURN(pool && clock_rate && channel_count && + PJ_ASSERT_RETURN(pool_ && clock_rate && channel_count && samples_per_frame && bits_per_sample && p_splitcomb, PJ_EINVAL); @@ -224,9 +227,14 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool, *p_splitcomb = NULL; + /* Create own pool */ + pool = pj_pool_create(pool_->factory, "splitcomb", 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + /* Create the splitter/combiner structure */ sc = PJ_POOL_ZALLOC_T(pool, struct splitcomb); - PJ_ASSERT_RETURN(sc != NULL, PJ_ENOMEM); + PJ_ASSERT_ON_FAIL(sc != NULL, {pj_pool_release(pool);return PJ_ENOMEM;}); + sc->pool = pool; /* Create temporary buffers */ sc->get_buf = (TMP_SAMP_TYPE*) @@ -253,6 +261,13 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create( pj_pool_t *pool, sc->base.get_frame = &get_frame; sc->base.on_destroy = &on_destroy; + /* Create group lock */ + status = pjmedia_port_init_grp_lock(&sc->base, pool, NULL); + if (status != PJ_SUCCESS) { + pj_pool_release(pool); + return status; + } + /* Init ports array */ /* sc->port_desc = pj_pool_zalloc(pool, channel_count*sizeof(*sc->port_desc)); @@ -291,6 +306,16 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_set_channel( pjmedia_port *splitcomb, sc->port_desc[ch_num].port = port; sc->port_desc[ch_num].reversed = PJ_FALSE; + if (!port->grp_lock) { + /* Create group lock if it does not have one. + * Note: don't use splitcomb's group lock as we will addref it here + * and only decref it from the destructor. + */ + pjmedia_port_init_grp_lock(port, sc->pool, NULL); + } + + pjmedia_port_add_ref(port); + return PJ_SUCCESS; } @@ -314,7 +339,8 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, pj_status_t status; /* Sanity check */ - PJ_ASSERT_RETURN(pool && splitcomb, PJ_EINVAL); + PJ_ASSERT_RETURN(splitcomb, PJ_EINVAL); + PJ_UNUSED_ARG(pool); /* Make sure this is really a splitcomb port */ PJ_ASSERT_RETURN(sc->base.info.signature == SIGNATURE, PJ_EINVAL); @@ -328,7 +354,7 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, sc_afd = pjmedia_format_get_audio_format_detail(&splitcomb->info.fmt, 1); /* Create the port */ - rport = PJ_POOL_ZALLOC_T(pool, struct reverse_port); + rport = PJ_POOL_ZALLOC_T(sc->pool, struct reverse_port); rport->parent = sc; rport->ch_num = ch_num; @@ -392,6 +418,12 @@ PJ_DEF(pj_status_t) pjmedia_splitcomb_create_rev_channel( pj_pool_t *pool, sc->port_desc[ch_num].port = &rport->base; sc->port_desc[ch_num].reversed = PJ_TRUE; + /* Init group lock */ + status = pjmedia_port_init_grp_lock(port, sc->pool, sc->base.grp_lock); + if (status != PJ_SUCCESS) { + rport_on_destroy(&rport->base); + return status; + } /* Done */ *p_chport = port; @@ -688,10 +720,20 @@ static pj_status_t get_frame(pjmedia_port *this_port, static pj_status_t on_destroy(pjmedia_port *this_port) { - /* Nothing to do for the splitcomb - * Reverse ports must be destroyed separately. - */ - PJ_UNUSED_ARG(this_port); + struct splitcomb *sc = (struct splitcomb*) this_port; + unsigned ch; + + /* Decrement reference for non-reserved channels */ + for (ch=0; ch < PJMEDIA_PIA_CCNT(&this_port->info); ++ch) { + pjmedia_port *port = sc->port_desc[ch].port; + + if (!port || sc->port_desc[ch].reversed) + continue; + + pjmedia_port_dec_ref(port); + } + + pj_pool_release(sc->pool); return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c index d5d62aa431..4bf8e6a761 100644 --- a/pjmedia/src/pjmedia/stream.c +++ b/pjmedia/src/pjmedia/stream.c @@ -990,7 +990,7 @@ static void create_dtmf_payload(pjmedia_stream *stream, event = (pjmedia_rtp_dtmf_event*) frame_out->buf; if (digit->duration == 0) { - PJ_LOG(5,(stream->port.info.name.ptr, "Sending DTMF digit id %c", + PJ_LOG(4,(stream->port.info.name.ptr, "Sending DTMF digit id %c", digitmap[digit->event])); *first = 1; } @@ -2455,7 +2455,8 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, PJ_ASSERT_RETURN(endpt && info && p_stream, PJ_EINVAL); - if (1 || pool == NULL) { + /* Must create own pool to avoid premature destroy */ + if (1 /* || pool == NULL */) { own_pool = pjmedia_endpt_create_pool( endpt, "strm%p", PJMEDIA_STREAM_SIZE, PJMEDIA_STREAM_INC); @@ -2492,8 +2493,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, 16, 80); afd = pjmedia_format_get_audio_format_detail(&stream->port.info.fmt, 1); - /* Init port. */ - //No longer there in 2.0 //pj_strdup(pool, &stream->port.info.encoding_name, &info->fmt.encoding_name); afd->clock_rate = info->fmt.clock_rate; @@ -2904,7 +2903,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt, /* Attach handler to group lock from transport */ if (tp->grp_lock) { - stream->grp_lock = tp->grp_lock; + stream->grp_lock = stream->port.grp_lock = tp->grp_lock; pj_grp_lock_add_ref(stream->grp_lock); pj_grp_lock_add_handler(stream->grp_lock, pool, stream, &stream_on_destroy); @@ -3077,6 +3076,37 @@ static void stream_on_destroy(void *arg) { pjmedia_stream* stream = (pjmedia_stream*)arg; + /* This function may be called when stream is partly initialized. */ + if (stream->jb_mutex) + pj_mutex_lock(stream->jb_mutex); + + /* Free codec. */ + + if (stream->codec) { + pjmedia_codec_close(stream->codec); + pjmedia_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec); + stream->codec = NULL; + } + + /* Free mutex */ + + if (stream->jb_mutex) { + pj_mutex_unlock(stream->jb_mutex); + pj_mutex_destroy(stream->jb_mutex); + stream->jb_mutex = NULL; + } + + /* Destroy jitter buffer */ + if (stream->jb) + pjmedia_jbuf_destroy(stream->jb); + +#if TRACE_JB + if (TRACE_JB_OPENED(stream)) { + pj_file_close(stream->trace_jb_fd); + stream->trace_jb_fd = TRACE_JB_INVALID_FD; + } +#endif + PJ_LOG(4,(stream->port.info.name.ptr, "Stream destroyed")); pj_pool_safe_release(&stream->own_pool); } @@ -3156,41 +3186,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream ) */ if (stream->transport) { pjmedia_transport_detach(stream->transport, stream); - stream->transport = NULL; + //stream->transport = NULL; } - /* This function may be called when stream is partly initialized. */ - if (stream->jb_mutex) - pj_mutex_lock(stream->jb_mutex); - - - /* Free codec. */ - - if (stream->codec) { - pjmedia_codec_close(stream->codec); - pjmedia_codec_mgr_dealloc_codec(stream->codec_mgr, stream->codec); - stream->codec = NULL; - } - - /* Free mutex */ - - if (stream->jb_mutex) { - pj_mutex_unlock(stream->jb_mutex); - pj_mutex_destroy(stream->jb_mutex); - stream->jb_mutex = NULL; - } - - /* Destroy jitter buffer */ - if (stream->jb) - pjmedia_jbuf_destroy(stream->jb); - -#if TRACE_JB - if (TRACE_JB_OPENED(stream)) { - pj_file_close(stream->trace_jb_fd); - stream->trace_jb_fd = TRACE_JB_INVALID_FD; - } -#endif - if (stream->grp_lock) { pj_grp_lock_dec_ref(stream->grp_lock); } else { diff --git a/pjmedia/src/pjmedia/tonegen.c b/pjmedia/src/pjmedia/tonegen.c index ea667818fb..9cd26d390d 100644 --- a/pjmedia/src/pjmedia/tonegen.c +++ b/pjmedia/src/pjmedia/tonegen.c @@ -351,6 +351,7 @@ enum flags struct tonegen { pjmedia_port base; + pj_pool_t *pool; /* options */ unsigned options; @@ -409,7 +410,7 @@ static pj_status_t tonegen_destroy(pjmedia_port *this_port); * When the tone generator is first created, it will be loaded with the * default digit map. */ -PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool_, const pj_str_t *name, unsigned clock_rate, unsigned channel_count, @@ -420,23 +421,31 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool, { const pj_str_t STR_TONE_GEN = pj_str("tonegen"); struct tonegen *tonegen; - pj_status_t status; + pj_status_t status = PJ_SUCCESS; + pj_pool_t *pool = NULL; - PJ_ASSERT_RETURN(pool && clock_rate && channel_count && + PJ_ASSERT_RETURN(pool_ && clock_rate && channel_count && samples_per_frame && bits_per_sample == 16 && p_port != NULL, PJ_EINVAL); /* Only support mono and stereo */ PJ_ASSERT_RETURN(channel_count==1 || channel_count==2, PJ_EINVAL); + if (name == NULL || name->slen == 0) + name = &STR_TONE_GEN; + + /* Create own pool */ + pool = pj_pool_create(pool_->factory, name->ptr, 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + /* Create and initialize port */ tonegen = PJ_POOL_ZALLOC_T(pool, struct tonegen); - if (name == NULL || name->slen == 0) name = &STR_TONE_GEN; + tonegen->pool = pool; status = pjmedia_port_info_init(&tonegen->base.info, name, SIGNATURE, clock_rate, channel_count, bits_per_sample, samples_per_frame); if (status != PJ_SUCCESS) - return status; + goto on_return; tonegen->options = options; tonegen->base.get_frame = &tonegen_get_frame; @@ -454,7 +463,7 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool, } if (status != PJ_SUCCESS) { - return status; + goto on_return; } TRACE_((THIS_FILE, "Tonegen created: %u/%u/%u/%u", clock_rate, @@ -462,7 +471,13 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_create2(pj_pool_t *pool, /* Done */ *p_port = &tonegen->base; - return PJ_SUCCESS; + +on_return: + if (status != PJ_SUCCESS) { + if (pool) + pj_pool_release(pool); + } + return status; } @@ -911,6 +926,9 @@ PJ_DEF(pj_status_t) pjmedia_tonegen_set_digit_map(pjmedia_port *port, pj_lock_release(tonegen->lock); + if (tonegen->pool) + pj_pool_safe_release(&tonegen->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/vid_conf.c b/pjmedia/src/pjmedia/vid_conf.c index 7509f013c2..2620b21b0b 100644 --- a/pjmedia/src/pjmedia/vid_conf.c +++ b/pjmedia/src/pjmedia/vid_conf.c @@ -93,6 +93,7 @@ typedef struct vconf_port unsigned idx; /**< Port index. */ pj_str_t name; /**< Port name. */ pjmedia_port *port; /**< Video port. */ + pj_bool_t is_new; /**< Port newly added? */ pjmedia_format format; /**< Copy of port format info. */ pj_uint32_t ts_interval; /**< Port put/get interval. */ pj_timestamp ts_next; /**< Time for next put/get_frame(). */ @@ -340,6 +341,9 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_destroy(pjmedia_vid_conf *vid_conf) vid_conf->clock = NULL; } + /* Flush any pending operation (connect, disconnect, etc) */ + handle_op_queue(vid_conf); + /* Remove any registered ports (at least to cleanup their pool) */ for (i=0; i < vid_conf->opt.max_slot_cnt; ++i) { if (vid_conf->ports[i]) { @@ -395,19 +399,16 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_add_port( pjmedia_vid_conf *vid_conf, pj_mutex_lock(vid_conf->mutex); - if (vid_conf->port_cnt >= vid_conf->opt.max_slot_cnt) { - PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY, "Add port %s failed", name->ptr)); - pj_assert(!"Too many ports"); - pj_mutex_unlock(vid_conf->mutex); - return PJ_ETOOMANY; - } - /* Find empty port in the conference bridge. */ for (index=0; index < vid_conf->opt.max_slot_cnt; ++index) { if (vid_conf->ports[index] == NULL) break; } - pj_assert(index != vid_conf->opt.max_slot_cnt); + if (index == vid_conf->opt.max_slot_cnt) { + PJ_PERROR(3,(THIS_FILE, PJ_ETOOMANY, "Add port %s failed", name->ptr)); + pj_mutex_unlock(vid_conf->mutex); + return PJ_ETOOMANY; + } /* Create pool */ pool = pj_pool_create(parent_pool->factory, name->ptr, 500, 500, NULL); @@ -553,9 +554,14 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_add_port( pjmedia_vid_conf *vid_conf, goto on_error; } - /* Register the conf port. */ + /* Video data flow is not protected, avoid processing this newly + * added port. + */ + cport->is_new = PJ_TRUE; + + /* Register the conf port, but don't add port counter yet */ vid_conf->ports[index] = cport; - vid_conf->port_cnt++; + //vid_conf->port_cnt++; PJ_LOG(4,(THIS_FILE,"Added port %d (%.*s)", index, (int)cport->name.slen, cport->name.ptr)); @@ -1032,6 +1038,17 @@ static void on_clock_tick(const pj_timestamp *now, void *user_data) if (!pj_list_empty(vid_conf->op_queue)) { pj_mutex_lock(vid_conf->mutex); handle_op_queue(vid_conf); + + /* Activate any newly added port */ + for (i=0; iopt.max_slot_cnt; ++i) { + vconf_port *port = vid_conf->ports[i]; + if (!port || !port->is_new) + continue; + + port->is_new = PJ_FALSE; + ++vid_conf->port_cnt; + } + pj_mutex_unlock(vid_conf->mutex); } @@ -1054,8 +1071,8 @@ static void on_clock_tick(const pj_timestamp *now, void *user_data) vconf_port *sink = vid_conf->ports[i]; pjmedia_format *cur_fmt, *new_fmt; - /* Skip empty port */ - if (!sink) + /* Skip empty or new port */ + if (!sink || sink->is_new) continue; /* Increment occupied port counter */ diff --git a/pjmedia/src/pjmedia/wav_player.c b/pjmedia/src/pjmedia/wav_player.c index 410cf26272..774ddda180 100644 --- a/pjmedia/src/pjmedia/wav_player.c +++ b/pjmedia/src/pjmedia/wav_player.c @@ -55,6 +55,7 @@ struct file_reader_port { pjmedia_port base; + pj_pool_t *pool; unsigned options; pjmedia_wave_fmt_tag fmt_tag; pj_uint16_t bytes_per_sample; @@ -87,8 +88,10 @@ static struct file_reader_port *create_file_port(pj_pool_t *pool) struct file_reader_port *port; port = PJ_POOL_ZALLOC_T(pool, struct file_reader_port); - if (!port) + if (!port) { + pj_pool_release(pool); return NULL; + } /* Put in default values. * These will be overriden once the file is read. @@ -217,7 +220,7 @@ static pj_status_t read_wav_until(struct file_reader_port *fport, /* * Create WAVE player port. */ -PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool_, const char *filename, unsigned ptime, unsigned options, @@ -234,14 +237,16 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, unsigned samples_per_frame; pjmedia_wave_subchunk chunk; pj_status_t status = PJ_SUCCESS; + pj_pool_t *pool = NULL; /* Check arguments. */ - PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL); + PJ_ASSERT_RETURN(pool_ && filename && p_port, PJ_EINVAL); /* Check the file really exists. */ if (!pj_file_exists(filename)) { - return PJ_ENOTFOUND; + status = PJ_ENOTFOUND; + goto on_error; } /* Normalize ptime */ @@ -252,11 +257,20 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, if (buff_size < 1) buff_size = PJMEDIA_FILE_PORT_BUFSIZE; + /* Create own pool */ + pool = pj_pool_create(pool_->factory, filename, 500, 500, NULL); + if (!pool) { + status = PJ_ENOMEM; + goto on_error; + } + /* Create fport instance. */ fport = create_file_port(pool); if (!fport) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } + fport->pool = pool; /* Get the file size. */ @@ -264,25 +278,28 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, /* Size must be more than WAVE header size */ if (fport->fsize <= (pj_off_t)sizeof(pjmedia_wave_hdr)) { - return PJMEDIA_ENOTVALIDWAVE; + status = PJMEDIA_ENOTVALIDWAVE; + goto on_error; } /* Open file. */ status = pj_file_open(pool, filename, PJ_O_RDONLY | PJ_O_CLOEXEC, &fport->fd); if (status != PJ_SUCCESS) - return status; + goto on_error; + /* Read the RIFF file header only. */ size_to_read = size_read = sizeof(wave_hdr.riff_hdr); status = pj_file_read( fport->fd, &wave_hdr, &size_read); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } if (size_read != size_to_read) { pj_file_close(fport->fd); - return PJMEDIA_ENOTVALIDWAVE; + status = PJMEDIA_ENOTVALIDWAVE; + goto on_error; } /* Normalize WAVE header fields values from little-endian to host @@ -299,14 +316,15 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, "actual value|expected riff=%x|%x, wave=%x|%x", wave_hdr.riff_hdr.riff, PJMEDIA_RIFF_TAG, wave_hdr.riff_hdr.wave, PJMEDIA_WAVE_TAG)); - return PJMEDIA_ENOTVALIDWAVE; + status = PJMEDIA_ENOTVALIDWAVE; + goto on_error; } /* Read the WAVE file until we find 'fmt ' chunk. */ status = read_wav_until(fport, PJMEDIA_FMT_TAG, &chunk); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } pj_memcpy(&wave_hdr.fmt_hdr, &chunk, sizeof(chunk)); @@ -316,7 +334,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, status = pj_file_read(fport->fd, &wave_hdr.fmt_hdr.fmt_tag, &size_read); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } pjmedia_wave_hdr_file_to_host(&wave_hdr); @@ -343,7 +361,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } fport->fmt_tag = (pjmedia_wave_fmt_tag)wave_hdr.fmt_hdr.fmt_tag; @@ -360,7 +378,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, status = pj_file_setpos(fport->fd, size_to_read, PJ_SEEK_CUR); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } } @@ -368,7 +386,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, status = read_wav_until(fport, PJMEDIA_DATA_TAG, &chunk); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } PJMEDIA_WAVE_NORMALIZE_SUBCHUNK(&chunk); @@ -394,7 +412,8 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, wave_hdr.fmt_hdr.nchan / 1000) { pj_file_close(fport->fd); - return PJMEDIA_EWAVETOOSHORT; + status = PJMEDIA_EWAVETOOSHORT; + goto on_error; } /* It seems like we have a valid WAVE file. */ @@ -430,14 +449,16 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, */ if (samples_per_frame * fport->bytes_per_sample > fport->bufsize) { pj_file_close(fport->fd); - return PJ_EINVAL; + status = PJ_EINVAL; + goto on_error; } /* Create buffer. */ fport->buf = (char*) pj_pool_alloc(pool, fport->bufsize); if (!fport->buf) { pj_file_close(fport->fd); - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } fport->readpos = fport->buf; @@ -449,7 +470,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, status = fill_buffer(fport); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } /* Done. */ @@ -468,6 +489,15 @@ PJ_DEF(pj_status_t) pjmedia_wav_player_port_create( pj_pool_t *pool, (unsigned long)(fport->fsize / 1000))); return PJ_SUCCESS; + +on_error: + if (pool) + pj_pool_release(pool); + + PJ_PERROR(1,(THIS_FILE, status, + "Failed creating file player '%s'", filename)); + + return status; } @@ -857,6 +887,9 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) fport->subscribed = PJ_FALSE; } + if (fport->pool) + pj_pool_safe_release(&fport->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/wav_playlist.c b/pjmedia/src/pjmedia/wav_playlist.c index edd75d5ef4..7ecbe00c46 100644 --- a/pjmedia/src/pjmedia/wav_playlist.c +++ b/pjmedia/src/pjmedia/wav_playlist.c @@ -57,6 +57,7 @@ struct playlist_port { pjmedia_port base; + pj_pool_t *pool; unsigned options; pj_bool_t eof; pj_uint32_t bufsize; @@ -292,7 +293,7 @@ static pj_status_t file_fill_buffer(struct playlist_port *fport) /* * Create wave list player. */ -PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool_, const pj_str_t *port_label, const pj_str_t file_list[], int file_count, @@ -304,15 +305,16 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, struct playlist_port *fport; pjmedia_audio_format_detail *afd; pj_off_t pos; - pj_status_t status; + pj_status_t status = PJ_SUCCESS; int index; pj_bool_t has_wave_info = PJ_FALSE; pj_str_t tmp_port_label; char filename[PJ_MAXPATH]; /* filename for open operations. */ + pj_pool_t *pool = NULL; /* Check arguments. */ - PJ_ASSERT_RETURN(pool && file_list && file_count && p_port, PJ_EINVAL); + PJ_ASSERT_RETURN(pool_ && file_list && file_count && p_port, PJ_EINVAL); /* Normalize port_label */ if (port_label == NULL || port_label->slen == 0) { @@ -343,11 +345,17 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, if (ptime == 0) ptime = 20; + /* Create own pool */ + pool = pj_pool_create(pool_->factory, port_label->ptr, 1024, 1024, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + /* Create fport instance. */ fport = create_file_list_port(pool, port_label); if (!fport) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } + fport->pool = pool; afd = pjmedia_format_get_audio_format_detail(&fport->base.info.fmt, 1); @@ -359,42 +367,48 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, fport->fd_list = (pj_oshandle_t*) pj_pool_zalloc(pool, sizeof(pj_oshandle_t)*file_count); if (!fport->fd_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create file size list */ fport->fsize_list = (pj_off_t*) pj_pool_alloc(pool, sizeof(pj_off_t)*file_count); if (!fport->fsize_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create start of WAVE data list */ fport->start_data_list = (unsigned*) pj_pool_alloc(pool, sizeof(unsigned)*file_count); if (!fport->start_data_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create data len list */ fport->data_len_list = (unsigned*) pj_pool_alloc(pool, sizeof(unsigned)*file_count); if (!fport->data_len_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create data left list */ fport->data_left_list = (unsigned*) pj_pool_alloc(pool, sizeof(unsigned)*file_count); if (!fport->data_left_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create file position list */ fport->fpos_list = (pj_off_t*) pj_pool_alloc(pool, sizeof(pj_off_t)*file_count); if (!fport->fpos_list) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Create file buffer once for this operation. @@ -406,7 +420,8 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, /* Create buffer. */ fport->buf = (char*) pj_pool_alloc(pool, fport->bufsize); if (!fport->buf) { - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } /* Initialize port */ @@ -628,12 +643,21 @@ PJ_DEF(pj_status_t) pjmedia_wav_playlist_create(pj_pool_t *pool, return PJ_SUCCESS; + on_error: + for (index=0; indexfd_list[index] != 0) pj_file_close(fport->fd_list[index]); } + if (pool) + pj_pool_release(pool); + + PJ_PERROR(1,(THIS_FILE, status, + "Failed creating WAV playlist '%s'", + (int)port_label->slen, port_label->ptr)); + return status; } @@ -775,6 +799,9 @@ static pj_status_t file_list_on_destroy(pjmedia_port *this_port) for (index=0; indexmax_file; index++) pj_file_close(fport->fd_list[index]); + if (fport->pool) + pj_pool_safe_release(&fport->pool); + return PJ_SUCCESS; } diff --git a/pjmedia/src/pjmedia/wav_writer.c b/pjmedia/src/pjmedia/wav_writer.c index dbe491c9ce..fefc2baf56 100644 --- a/pjmedia/src/pjmedia/wav_writer.c +++ b/pjmedia/src/pjmedia/wav_writer.c @@ -35,6 +35,7 @@ struct file_port { pjmedia_port base; + pj_pool_t *pool; pjmedia_wave_fmt_tag fmt_tag; pj_uint16_t bytes_per_sample; @@ -62,7 +63,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port); /* * Create file writer port. */ -PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, +PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool_, const char *filename, unsigned sampling_rate, unsigned channel_count, @@ -77,18 +78,27 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, pj_ssize_t size; pj_str_t name; pj_status_t status; + pj_pool_t *pool = NULL; /* Check arguments. */ - PJ_ASSERT_RETURN(pool && filename && p_port, PJ_EINVAL); + PJ_ASSERT_RETURN(pool_ && filename && p_port, PJ_EINVAL); /* Only supports 16bits per sample for now. * See flush_buffer(). */ PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL); + /* Create own pool */ + pool = pj_pool_create(pool_->factory, filename, 500, 500, NULL); + PJ_ASSERT_RETURN(pool, PJ_ENOMEM); + /* Create file port instance. */ fport = PJ_POOL_ZALLOC_T(pool, struct file_port); - PJ_ASSERT_RETURN(fport != NULL, PJ_ENOMEM); + if (!fport) { + status = PJ_ENOMEM; + goto on_error; + } + fport->pool = pool; /* Initialize port info. */ pj_strdup2(pool, &name, filename); @@ -118,7 +128,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, status = pj_file_open(pool, filename, PJ_O_WRONLY | PJ_O_CLOEXEC, &fport->fd); if (status != PJ_SUCCESS) - return status; + goto on_error; /* Initialize WAVE header */ pj_bzero(&wave_hdr, sizeof(pjmedia_wave_hdr)); @@ -163,7 +173,7 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, status = pj_file_write(fport->fd, &wave_hdr, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } /* Write FACT chunk if it stores compressed data */ @@ -171,13 +181,13 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, status = pj_file_write(fport->fd, &fact_chunk, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } size = 4; status = pj_file_write(fport->fd, &tmp, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } /* Write DATA chunk header */ @@ -185,14 +195,14 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, status = pj_file_write(fport->fd, &wave_hdr.data_hdr, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } } else { size = sizeof(pjmedia_wave_hdr); status = pj_file_write(fport->fd, &wave_hdr, &size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_error; } } @@ -208,7 +218,8 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, fport->buf = (char*) pj_pool_alloc(pool, fport->bufsize); if (fport->buf == NULL) { pj_file_close(fport->fd); - return PJ_ENOMEM; + status = PJ_ENOMEM; + goto on_error; } fport->writepos = fport->buf; @@ -224,6 +235,17 @@ PJ_DEF(pj_status_t) pjmedia_wav_writer_port_create( pj_pool_t *pool, return PJ_SUCCESS; + + +on_error: + + if (pool) + pj_pool_release(pool); + + PJ_PERROR(1,(THIS_FILE, status, + "Failed creating WAV writer '%s'", filename)); + + return status; } @@ -461,7 +483,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) pj_ssize_t bytes; pj_uint32_t wave_file_len; pj_uint32_t wave_data_len; - pj_status_t status; + pj_status_t status = PJ_SUCCESS; pj_uint32_t data_len_pos = DATA_LEN_POS; if (fport->subscribed) { @@ -477,7 +499,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_getpos(fport->fd, &file_size); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Calculate wave fields */ @@ -493,7 +515,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_setpos(fport->fd, FILE_LEN_POS, PJ_SEEK_SET); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Write file_len */ @@ -501,7 +523,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_write(fport->fd, &wave_file_len, &bytes); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Write samples_len in FACT chunk */ @@ -518,7 +540,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_setpos(fport->fd, SAMPLES_LEN_POS, PJ_SEEK_SET); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Write samples_len */ @@ -526,7 +548,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_write(fport->fd, &wav_samples_len, &bytes); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } } @@ -534,7 +556,7 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_setpos(fport->fd, data_len_pos, PJ_SEEK_SET); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Write file_len */ @@ -542,15 +564,18 @@ static pj_status_t file_on_destroy(pjmedia_port *this_port) status = pj_file_write(fport->fd, &wave_data_len, &bytes); if (status != PJ_SUCCESS) { pj_file_close(fport->fd); - return status; + goto on_return; } /* Close file */ status = pj_file_close(fport->fd); if (status != PJ_SUCCESS) - return status; + goto on_return; - /* Done. */ - return PJ_SUCCESS; +on_return: + if (fport->pool) + pj_pool_safe_release(&fport->pool); + + return status; } diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index 83c41cfe94..26764ab0d5 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -1389,6 +1389,14 @@ void legacy_on_stopped(pj_bool_t restart) (*app_cfg.on_stopped)(restart, 1, NULL); } + +static void app_cleanup(pjsip_endpoint *endpt) +{ + PJ_UNUSED_ARG(endpt); + pj_pool_safe_release(&app_config.pool); +} + + /***************************************************************************** * Public API */ @@ -1430,7 +1438,10 @@ static pj_status_t app_init(void) /* Create pool for application */ app_config.pool = pjsua_pool_create("pjsua-app", 1000, 1000); - tmp_pool = pjsua_pool_create("tmp-pjsua", 1000, 1000);; + tmp_pool = pjsua_pool_create("tmp-pjsua", 1000, 1000); + + /* Queue pool release at PJLIB exit */ + pjsip_endpt_atexit(pjsua_get_pjsip_endpt(), &app_cleanup); /* Init CLI & its FE settings */ if (!app_running) { @@ -2190,7 +2201,15 @@ static pj_status_t app_destroy(void) pjsip_tls_setting_wipe_keys(&app_config.udp_cfg.tls_setting); #endif - pj_pool_safe_release(&app_config.pool); + /* The pool release has been scheduled via pjsip_endpt_atexit(). + * + * We can only release the pool after audio & video conference destroy. + * Note that pjsua_conf_remove_port()/pjsua_vid_conf_remove_port() + * is asynchronous, so when sound device is not active, PJMEDIA ports + * have not been removed from the conference (and destroyed) yet + * until the audio & video conferences are destroyed (in pjsua_destroy()). + */ + //pj_pool_safe_release(&app_config.pool); status = pjsua_destroy(); diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index 591be2b83b..09e562d492 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -606,8 +606,15 @@ void pjsua_aud_stop_stream(pjsua_call_media *call_med) } if (call_med->strm.a.media_port) { - if (call_med->strm.a.destroy_port) + pjmedia_port *stream_port; + + /* Destroy custom stream port if any & configured to */ + pjmedia_stream_get_port(call_med->strm.a.stream, &stream_port); + if (call_med->strm.a.destroy_port && + call_med->strm.a.media_port != stream_port) + { pjmedia_port_destroy(call_med->strm.a.media_port); + } call_med->strm.a.media_port = NULL; }