⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 conference.c

📁 一个开源的sip源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* $Id: conference.c 1301 2007-05-25 06:13:55Z 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_FILE
static 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_T(pool, 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 = (SLOT_TYPE*)
				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->channel_count,
					  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->channel_count,
					 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_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);

	/* Create TX buffer. */
	conf_port->tx_buf_cap = conf_port->rx_buf_cap;
	conf_port->tx_buf_count = 0;
	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);
    }


    /* Create mix buffer. */
    conf_port->mix_buf = (pj_uint32_t*)
			 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_int16_t*)
				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);

⌨️ 快捷键说明

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