conference.c

来自「基于sip协议的网络电话源码」· C语言 代码 · 共 1,932 行 · 第 1/4 页

C
1,932
字号
/* $Id: conference.c 974 2007-02-19 01:13:53Z bennylp $ *//*  * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  */#include <pjmedia/conference.h>#include <pjmedia/alaw_ulaw.h>#include <pjmedia/errno.h>#include <pjmedia/port.h>#include <pjmedia/resample.h>#include <pjmedia/silencedet.h>#include <pjmedia/sound_port.h>#include <pjmedia/stream.h>#include <pj/array.h>#include <pj/assert.h>#include <pj/log.h>#include <pj/pool.h>#include <pj/string.h>/* CONF_DEBUG enables detailed operation of the conference bridge. * Beware that it prints large amounts of logs (several lines per frame). *///#define CONF_DEBUG#ifdef CONF_DEBUG#   include <stdio.h>#   define TRACE_(x)   PJ_LOG(5,x)#else#   define TRACE_(x)#endif/* REC_FILE macro enables recording of the samples written to the sound * device. The file contains RAW PCM data with no header, and has the * same settings (clock rate etc) as the conference bridge. * This should only be enabled when debugging audio quality *only*. *///#define REC_FILE    "confrec.pcm"#ifdef REC_FILEstatic FILE *fhnd_rec;#endif#define THIS_FILE	"conference.c"#define RX_BUF_COUNT	PJMEDIA_SOUND_BUFFER_COUNT#define BYTES_PER_SAMPLE    2#define SIGNATURE	    PJMEDIA_PORT_SIGNATURE('C', 'O', 'N', 'F')#define SIGNATURE_PORT	    PJMEDIA_PORT_SIGNATURE('C', 'O', 'N', 'P')/* Normal level is hardcodec to 128 in all over places */#define NORMAL_LEVEL	    128#define SLOT_TYPE	    unsigned#define INVALID_SLOT	    ((SLOT_TYPE)-1)/* These are settings to control the adaptivity of changes in the * signal level of the ports, so that sudden change in signal level * in the port does not cause misaligned signal (which causes noise). */#if 1#   define ATTACK_A	    10#   define ATTACK_B	    2#   define DECAY_A	    10#   define DECAY_B	    2#else    /* To simulate old behavior */#   define ATTACK_A	    0#   define ATTACK_B	    1#   define DECAY_A	    0#   define DECAY_B	    1#endif/* * DON'T GET CONFUSED WITH TX/RX!! * * TX and RX directions are always viewed from the conference bridge's point * of view, and NOT from the port's point of view. So TX means the bridge * is transmitting to the port, RX means the bridge is receiving from the * port. *//** * This is a port connected to conference bridge. */struct conf_port{    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  */    pjmedia_port_op	 tx_setting;	/**< Can we transmit to this port   */    unsigned		 listener_cnt;	/**< Number of listeners.	    */    SLOT_TYPE		*listener_slots;/**< Array of listeners.	    */    unsigned		 transmitter_cnt;/**<Number of transmitters.	    */    pjmedia_silence_det	*vad;		/**< VAD for this port.		    */    /* Shortcut for port info. */    unsigned		 clock_rate;	/**< Port's clock rate.		    */    unsigned		 samples_per_frame; /**< Port's samples per frame.  */    /* Last level calculated from this port */    pj_int32_t		 last_level;    /* Calculated signal levels: */    unsigned		 tx_level;	/**< Last tx level to this port.    */    unsigned		 rx_level;	/**< Last rx level from this port.  */    /* The normalized signal level adjustment.     * A value of 128 (NORMAL_LEVEL) means there's no adjustment.     */    unsigned		 tx_adj_level;	/**< Adjustment for TX.		    */    unsigned		 rx_adj_level;	/**< Adjustment for RX.		    */    /* Resample, for converting clock rate, if they're different. */    pjmedia_resample	*rx_resample;    pjmedia_resample	*tx_resample;    /* RX buffer is temporary buffer to be used when there is mismatch     * between port's sample rate or ptime with conference's sample rate     * or ptime. The buffer is used for sampling rate conversion AND/OR to     * buffer the samples until there are enough samples to fulfill a      * complete frame to be processed by the bridge.     *     * When both sample rate AND ptime of the port match the conference      * settings, this buffer will not be created.     *      * This buffer contains samples at port's clock rate.     * The size of this buffer is the sum between port's samples per frame     * and bridge's samples per frame.     */    pj_int16_t		*rx_buf;	/**< The RX buffer.		    */    unsigned		 rx_buf_cap;	/**< Max size, in samples	    */    unsigned		 rx_buf_count;	/**< # of samples in the buf.	    */    /* Mix buf is a temporary buffer used to calculate the average signal     * received by this port from all other ports. Samples from all ports     * that are transmitting to this port will be accumulated here, then     * they will be divided by the source level before the samples are put     * to the TX buffer of this port.     *     * This buffer contains samples at bridge's clock rate.     * The size of this buffer is equal to samples per frame of the bridge.     *     * Note that the samples here are unsigned 32bit.     */    unsigned		 src_level;	/**< Sum of input levels	    */    unsigned		 src_cnt;	/**< Number of sources.		    */    pj_uint32_t		*mix_buf;	/**< Total sum of signal.	    */    /* Tx buffer is a temporary buffer to be used when there's mismatch      * between port's clock rate or ptime with conference's sample rate     * or ptime. This buffer is used as the source of the sampling rate     * conversion AND/OR to buffer the samples until there are enough     * samples to fulfill a complete frame to be transmitted to the port.     *     * When both sample rate and ptime of the port match the bridge's      * settings, this buffer will not be created.     *      * This buffer contains samples at port's clock rate.     * The size of this buffer is the sum between port's samples per frame     * and bridge's samples per frame.     */    pj_int16_t		*tx_buf;	/**< Tx buffer.			    */    unsigned		 tx_buf_cap;	/**< Max size, in samples.	    */    unsigned		 tx_buf_count;	/**< # of samples in the buffer.    */    /* Snd buffers is a special buffer for sound device port (port 0, master     * port). It's not used by other ports.     *     * There are multiple numbers of this buffer, because we can not expect     * the mic and speaker thread to run equally after one another. In most     * systems, each thread will run multiple times before the other thread     * gains execution time. For example, in my system, mic thread is called     * three times, then speaker thread is called three times, and so on.     */    int			 snd_write_pos, snd_read_pos;    pj_int16_t		*snd_buf[RX_BUF_COUNT];	/**< Buffer 		    */};/* * Conference bridge. */struct pjmedia_conf{    unsigned		  options;	/**< Bitmask options.		    */    unsigned		  max_ports;	/**< Maximum ports.		    */    unsigned		  port_cnt;	/**< Current number of ports.	    */    unsigned		  connect_cnt;	/**< Total number of connections    */    pjmedia_snd_port	 *snd_dev_port;	/**< Sound device port.		    */    pjmedia_port	 *master_port;	/**< Port zero's port.		    */    char		  master_name_buf[80]; /**< Port0 name buffer.	    */    pj_mutex_t		 *mutex;	/**< Conference mutex.		    */    struct conf_port	**ports;	/**< Array of ports.		    */    pj_uint16_t		 *uns_buf;	/**< Buf for unsigned conversion    */    unsigned		  clock_rate;	/**< Sampling rate.		    */    unsigned		  channel_count;/**< Number of channels (1=mono).   */    unsigned		  samples_per_frame;	/**< Samples per frame.	    */    unsigned		  bits_per_sample;	/**< Bits per sample.	    */};/* Prototypes */static pj_status_t put_frame(pjmedia_port *this_port, 			     const pjmedia_frame *frame);static pj_status_t get_frame(pjmedia_port *this_port, 			     pjmedia_frame *frame);static pj_status_t get_frame_pasv(pjmedia_port *this_port, 				  pjmedia_frame *frame);static pj_status_t destroy_port(pjmedia_port *this_port);/* * Create port. */static pj_status_t create_conf_port( pj_pool_t *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;    /* Create port. */    conf_port = pj_pool_zalloc(pool, sizeof(struct conf_port));    PJ_ASSERT_RETURN(conf_port, PJ_ENOMEM);    /* Set name */    pj_strdup_with_null(pool, &conf_port->name, name);    /* Default has tx and rx enabled. */    conf_port->rx_setting = PJMEDIA_PORT_ENABLE;    conf_port->tx_setting = PJMEDIA_PORT_ENABLE;    /* Default level adjustment is 128 (which means no adjustment) */    conf_port->tx_adj_level = NORMAL_LEVEL;    conf_port->rx_adj_level = NORMAL_LEVEL;    /* Create transmit flag array */    conf_port->listener_slots = pj_pool_zalloc(pool, 					  conf->max_ports * sizeof(SLOT_TYPE));    PJ_ASSERT_RETURN(conf_port->listener_slots, PJ_ENOMEM);    /* Save some port's infos, for convenience. */    if (port) {	conf_port->port = port;	conf_port->clock_rate = port->info.clock_rate;	conf_port->samples_per_frame = port->info.samples_per_frame;    } else {	conf_port->port = NULL;	conf_port->clock_rate = conf->clock_rate;	conf_port->samples_per_frame = conf->samples_per_frame;    }    /* Create and init vad. */    status = pjmedia_silence_det_create( pool, 					 conf_port->clock_rate,					 conf_port->samples_per_frame,					 &conf_port->vad);    if (status != PJ_SUCCESS)	return status;    /* Set fixed */    pjmedia_silence_det_set_fixed(conf_port->vad, 2);    /* Set VAD name */    pjmedia_silence_det_set_name(conf_port->vad, conf_port->name.ptr);    /* If port's clock rate is different than conference's clock rate,     * create a resample sessions.     */    if (conf_port->clock_rate != conf->clock_rate) {	pj_bool_t high_quality;	pj_bool_t large_filter;	high_quality = ((conf->options & PJMEDIA_CONF_USE_LINEAR)==0);	large_filter = ((conf->options & PJMEDIA_CONF_SMALL_FILTER)==0);	/* Create resample for rx buffer. */	status = pjmedia_resample_create( pool, 					  high_quality,					  large_filter,					  conf_port->clock_rate,/* Rate in */					  conf->clock_rate, /* Rate out */					  conf->samples_per_frame * 					    conf_port->clock_rate /					    conf->clock_rate,					  &conf_port->rx_resample);	if (status != PJ_SUCCESS)	    return status;	/* Create resample for tx buffer. */	status = pjmedia_resample_create(pool,					 high_quality,					 large_filter,					 conf->clock_rate,  /* Rate in */					 conf_port->clock_rate, /* Rate out */					 conf->samples_per_frame,					 &conf_port->tx_resample);	if (status != PJ_SUCCESS)	    return status;    }    /*     * Initialize rx and tx buffer, only when port's samples per frame or      * port's clock rate is different then the conference bridge settings.     */    if (conf_port->clock_rate != conf->clock_rate ||	conf_port->samples_per_frame != conf->samples_per_frame)    {	/* Create RX buffer. */	conf_port->rx_buf_cap = (unsigned)(conf_port->samples_per_frame +					   conf->samples_per_frame * 					   conf_port->clock_rate * 1.0 /					   conf->clock_rate);	conf_port->rx_buf_count = 0;	conf_port->rx_buf = pj_pool_alloc(pool, conf_port->rx_buf_cap *						sizeof(conf_port->rx_buf[0]));	PJ_ASSERT_RETURN(conf_port->rx_buf, PJ_ENOMEM);	/* Create TX buffer. */	conf_port->tx_buf_cap = conf_port->rx_buf_cap;	conf_port->tx_buf_count = 0;	conf_port->tx_buf = pj_pool_alloc(pool, conf_port->tx_buf_cap *						sizeof(conf_port->tx_buf[0]));	PJ_ASSERT_RETURN(conf_port->tx_buf, PJ_ENOMEM);    }    /* Create mix buffer. */    conf_port->mix_buf = pj_pool_zalloc(pool, conf->samples_per_frame *					      sizeof(conf_port->mix_buf[0]));    PJ_ASSERT_RETURN(conf_port->mix_buf, PJ_ENOMEM);    /* Done */    *p_conf_port = conf_port;    return PJ_SUCCESS;}/* * Add passive port. */static pj_status_t create_pasv_port( pjmedia_conf *conf,				     pj_pool_t *pool,				     const pj_str_t *name,				     pjmedia_port *port,				     struct conf_port **p_conf_port){    struct conf_port *conf_port;    unsigned i;    pj_status_t status;    /* Create port */    status = create_conf_port(pool, conf, port, name, &conf_port);    if (status != PJ_SUCCESS)	return status;    /* Passive port has rx buffers. */    for (i=0; i<RX_BUF_COUNT; ++i) {	conf_port->snd_buf[i] = pj_pool_zalloc(pool, conf->samples_per_frame *					      sizeof(conf_port->snd_buf[0][0]));	if (conf_port->snd_buf[i] == NULL) {	    return PJ_ENOMEM;	}    }    conf_port->snd_write_pos = 0;    conf_port->snd_read_pos = 0;    *p_conf_port = conf_port;    return PJ_SUCCESS;}/* * Create port zero for the sound device. */static pj_status_t create_sound_port( pj_pool_t *pool,				      pjmedia_conf *conf ){    struct conf_port *conf_port;    pj_str_t name = { "Master/sound", 12 };    pj_status_t status;    status = create_pasv_port(conf, pool, &name, NULL, &conf_port);    if (status != PJ_SUCCESS)	return status;    /* Create sound device port: */    if ((conf->options & PJMEDIA_CONF_NO_DEVICE) == 0) {	pjmedia_snd_stream *strm;	pjmedia_snd_stream_info si;	/*	 * If capture is disabled then create player only port.	 * Otherwise create bidirectional sound device port.	 */	if (conf->options & PJMEDIA_CONF_NO_MIC)  {	    status = pjmedia_snd_port_create_player(pool, -1, conf->clock_rate,						    conf->channel_count,						    conf->samples_per_frame,						    conf->bits_per_sample, 						    0,	/* options */						    &conf->snd_dev_port);	} else {	    status = pjmedia_snd_port_create( pool, -1, -1, conf->clock_rate, 					      conf->channel_count, 					      conf->samples_per_frame,					      conf->bits_per_sample,					      0,    /* Options */					      &conf->snd_dev_port);	}	if (status != PJ_SUCCESS)	    return status;	strm = pjmedia_snd_port_get_snd_stream(conf->snd_dev_port);	status = pjmedia_snd_stream_get_info(strm, &si);	if (status == PJ_SUCCESS) {	    const pjmedia_snd_dev_info *snd_dev_info;	    if (conf->options & PJMEDIA_CONF_NO_MIC)		snd_dev_info = pjmedia_snd_get_dev_info(si.play_id);	    else		snd_dev_info = pjmedia_snd_get_dev_info(si.rec_id);	    pj_strdup2_with_null(pool, &conf_port->name, snd_dev_info->name);	}    }     /* Add the port to the bridge */    conf->ports[0] = conf_port;    conf->port_cnt++;    PJ_LOG(5,(THIS_FILE, "Sound device successfully created for port 0"));    return PJ_SUCCESS;}/* * Create conference bridge. */PJ_DEF(pj_status_t) pjmedia_conf_create( pj_pool_t *pool,					 unsigned max_ports,					 unsigned clock_rate,					 unsigned channel_count,					 unsigned samples_per_frame,					 unsigned bits_per_sample,					 unsigned options,					 pjmedia_conf **p_conf ){    pjmedia_conf *conf;    const pj_str_t name = { "Conf", 4 };    pj_status_t status;    /* Can only accept 16bits per sample, for now.. */    PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);    PJ_LOG(5,(THIS_FILE, "Creating conference bridge with %d ports",

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?